mirror of
https://github.com/flutter/samples.git
synced 2025-11-08 13:58:47 +00:00
platform_design: Enforce use_key_in_widget_constructors (#927)
This commit is contained in:
@@ -17,5 +17,3 @@ linter:
|
|||||||
test_types_in_equals: true
|
test_types_in_equals: true
|
||||||
throw_in_finally: true
|
throw_in_finally: true
|
||||||
unnecessary_statements: true
|
unnecessary_statements: true
|
||||||
# Tests fail if we enforce `use_key_in_widget_constructors`
|
|
||||||
use_key_in_widget_constructors: false
|
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ import 'settings_tab.dart';
|
|||||||
import 'songs_tab.dart';
|
import 'songs_tab.dart';
|
||||||
import 'widgets.dart';
|
import 'widgets.dart';
|
||||||
|
|
||||||
void main() => runApp(MyAdaptingApp());
|
void main() => runApp(const MyAdaptingApp());
|
||||||
|
|
||||||
class MyAdaptingApp extends StatelessWidget {
|
class MyAdaptingApp extends StatelessWidget {
|
||||||
|
const MyAdaptingApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(context) {
|
Widget build(context) {
|
||||||
// Either Material or Cupertino widgets work in either Material or Cupertino
|
// Either Material or Cupertino widgets work in either Material or Cupertino
|
||||||
@@ -34,6 +36,7 @@ class MyAdaptingApp extends StatelessWidget {
|
|||||||
child: Material(child: child),
|
child: Material(child: child),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
// ignore: use_key_in_widget_constructors
|
||||||
home: PlatformAdaptingHomePage(),
|
home: PlatformAdaptingHomePage(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -47,6 +50,8 @@ class MyAdaptingApp extends StatelessWidget {
|
|||||||
// These differences are also subjective and have more than one 'right' answer
|
// These differences are also subjective and have more than one 'right' answer
|
||||||
// depending on the app and content.
|
// depending on the app and content.
|
||||||
class PlatformAdaptingHomePage extends StatefulWidget {
|
class PlatformAdaptingHomePage extends StatefulWidget {
|
||||||
|
const PlatformAdaptingHomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_PlatformAdaptingHomePageState createState() =>
|
_PlatformAdaptingHomePageState createState() =>
|
||||||
_PlatformAdaptingHomePageState();
|
_PlatformAdaptingHomePageState();
|
||||||
@@ -107,12 +112,12 @@ class _PlatformAdaptingHomePageState extends State<PlatformAdaptingHomePage> {
|
|||||||
case 1:
|
case 1:
|
||||||
return CupertinoTabView(
|
return CupertinoTabView(
|
||||||
defaultTitle: NewsTab.title,
|
defaultTitle: NewsTab.title,
|
||||||
builder: (context) => NewsTab(),
|
builder: (context) => const NewsTab(),
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return CupertinoTabView(
|
return CupertinoTabView(
|
||||||
defaultTitle: ProfileTab.title,
|
defaultTitle: ProfileTab.title,
|
||||||
builder: (context) => ProfileTab(),
|
builder: (context) => const ProfileTab(),
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
assert(false, 'Unexpected tab');
|
assert(false, 'Unexpected tab');
|
||||||
@@ -161,8 +166,8 @@ class _AndroidDrawer extends StatelessWidget {
|
|||||||
title: const Text(NewsTab.title),
|
title: const Text(NewsTab.title),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
Navigator.push<void>(
|
Navigator.push<void>(context,
|
||||||
context, MaterialPageRoute(builder: (context) => NewsTab()));
|
MaterialPageRoute(builder: (context) => const NewsTab()));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
@@ -171,7 +176,7 @@ class _AndroidDrawer extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
Navigator.push<void>(context,
|
Navigator.push<void>(context,
|
||||||
MaterialPageRoute(builder: (context) => ProfileTab()));
|
MaterialPageRoute(builder: (context) => const ProfileTab()));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Long drawer contents are often segmented.
|
// Long drawer contents are often segmented.
|
||||||
@@ -185,7 +190,7 @@ class _AndroidDrawer extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
Navigator.push<void>(context,
|
Navigator.push<void>(context,
|
||||||
MaterialPageRoute(builder: (context) => SettingsTab()));
|
MaterialPageRoute(builder: (context) => const SettingsTab()));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class NewsTab extends StatefulWidget {
|
|||||||
static const androidIcon = Icon(Icons.library_books);
|
static const androidIcon = Icon(Icons.library_books);
|
||||||
static const iosIcon = Icon(CupertinoIcons.news);
|
static const iosIcon = Icon(CupertinoIcons.news);
|
||||||
|
|
||||||
|
const NewsTab({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_NewsTabState createState() => _NewsTabState();
|
_NewsTabState createState() => _NewsTabState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ class ProfileTab extends StatelessWidget {
|
|||||||
static const androidIcon = Icon(Icons.person);
|
static const androidIcon = Icon(Icons.person);
|
||||||
static const iosIcon = Icon(CupertinoIcons.profile_circled);
|
static const iosIcon = Icon(CupertinoIcons.profile_circled);
|
||||||
|
|
||||||
|
const ProfileTab({Key? key}) : super(key: key);
|
||||||
|
|
||||||
Widget _buildBody(BuildContext context) {
|
Widget _buildBody(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -55,7 +57,7 @@ class ProfileTab extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Container(),
|
child: Container(),
|
||||||
),
|
),
|
||||||
LogOutButton(),
|
const LogOutButton(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -89,7 +91,7 @@ class ProfileTab extends StatelessWidget {
|
|||||||
CupertinoPageRoute(
|
CupertinoPageRoute(
|
||||||
title: SettingsTab.title,
|
title: SettingsTab.title,
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (context) => SettingsTab(),
|
builder: (context) => const SettingsTab(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -113,7 +115,8 @@ class PreferenceCard extends StatelessWidget {
|
|||||||
required this.header,
|
required this.header,
|
||||||
required this.content,
|
required this.content,
|
||||||
required this.preferenceChoices,
|
required this.preferenceChoices,
|
||||||
});
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
final String header;
|
final String header;
|
||||||
final String content;
|
final String content;
|
||||||
@@ -171,6 +174,8 @@ class LogOutButton extends StatelessWidget {
|
|||||||
static const _logoutMessage = Text(
|
static const _logoutMessage = Text(
|
||||||
"You can't actually log out! This is just a demo of how alerts work.");
|
"You can't actually log out! This is just a demo of how alerts work.");
|
||||||
|
|
||||||
|
const LogOutButton({Key? key}) : super(key: key);
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Non-shared code below because this tab shows different interfaces. On
|
// Non-shared code below because this tab shows different interfaces. On
|
||||||
// Android, it's showing an alert dialog with 2 buttons and on iOS,
|
// Android, it's showing an alert dialog with 2 buttons and on iOS,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ class SettingsTab extends StatefulWidget {
|
|||||||
static const androidIcon = Icon(Icons.settings);
|
static const androidIcon = Icon(Icons.settings);
|
||||||
static const iosIcon = Icon(CupertinoIcons.gear);
|
static const iosIcon = Icon(CupertinoIcons.gear);
|
||||||
|
|
||||||
|
const SettingsTab({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_SettingsTabState createState() => _SettingsTabState();
|
_SettingsTabState createState() => _SettingsTabState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ class SongDetailTab extends StatelessWidget {
|
|||||||
required this.id,
|
required this.id,
|
||||||
required this.song,
|
required this.song,
|
||||||
required this.color,
|
required this.color,
|
||||||
});
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
final int id;
|
final int id;
|
||||||
final String song;
|
final String song;
|
||||||
@@ -71,7 +72,7 @@ class SongDetailTab extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Just a bunch of boxes that simulates loading song choices.
|
// Just a bunch of boxes that simulates loading song choices.
|
||||||
return SongPlaceholderTile();
|
return const SongPlaceholderTile();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ class PressableCard extends StatefulWidget {
|
|||||||
required this.color,
|
required this.color,
|
||||||
required this.flattenAnimation,
|
required this.flattenAnimation,
|
||||||
this.child,
|
this.child,
|
||||||
});
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
final Color color;
|
final Color color;
|
||||||
@@ -140,7 +141,8 @@ class HeroAnimatingSongCard extends StatelessWidget {
|
|||||||
required this.color,
|
required this.color,
|
||||||
required this.heroAnimation,
|
required this.heroAnimation,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
});
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
final String song;
|
final String song;
|
||||||
final Color color;
|
final Color color;
|
||||||
@@ -218,6 +220,8 @@ class HeroAnimatingSongCard extends StatelessWidget {
|
|||||||
/// This is an example of a custom widget that an app developer might create for
|
/// This is an example of a custom widget that an app developer might create for
|
||||||
/// use on both iOS and Android as part of their brand's unique design.
|
/// use on both iOS and Android as part of their brand's unique design.
|
||||||
class SongPlaceholderTile extends StatelessWidget {
|
class SongPlaceholderTile extends StatelessWidget {
|
||||||
|
const SongPlaceholderTile({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import 'package:platform_design/main.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Can change platform correctly', (tester) async {
|
testWidgets('Can change platform correctly', (tester) async {
|
||||||
await tester.pumpWidget(MyAdaptingApp());
|
await tester.pumpWidget(const MyAdaptingApp());
|
||||||
|
|
||||||
// The test should be able to find the drawer button.
|
// The test should be able to find the drawer button.
|
||||||
expect(find.byIcon(Icons.menu), findsOneWidget);
|
expect(find.byIcon(Icons.menu), findsOneWidget);
|
||||||
@@ -19,7 +19,7 @@ void main() {
|
|||||||
expect(find.byIcon(Icons.refresh), findsOneWidget);
|
expect(find.byIcon(Icons.refresh), findsOneWidget);
|
||||||
|
|
||||||
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||||
await tester.pumpWidget(MyAdaptingApp());
|
await tester.pumpWidget(const MyAdaptingApp());
|
||||||
|
|
||||||
// There should now be a large title style nav bar.
|
// There should now be a large title style nav bar.
|
||||||
expect(find.byType(CupertinoSliverNavigationBar), findsOneWidget);
|
expect(find.byType(CupertinoSliverNavigationBar), findsOneWidget);
|
||||||
|
|||||||
Reference in New Issue
Block a user