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
|
||||
throw_in_finally: 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 'widgets.dart';
|
||||
|
||||
void main() => runApp(MyAdaptingApp());
|
||||
void main() => runApp(const MyAdaptingApp());
|
||||
|
||||
class MyAdaptingApp extends StatelessWidget {
|
||||
const MyAdaptingApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
// Either Material or Cupertino widgets work in either Material or Cupertino
|
||||
@@ -34,6 +36,7 @@ class MyAdaptingApp extends StatelessWidget {
|
||||
child: Material(child: child),
|
||||
);
|
||||
},
|
||||
// ignore: use_key_in_widget_constructors
|
||||
home: PlatformAdaptingHomePage(),
|
||||
);
|
||||
}
|
||||
@@ -47,6 +50,8 @@ class MyAdaptingApp extends StatelessWidget {
|
||||
// These differences are also subjective and have more than one 'right' answer
|
||||
// depending on the app and content.
|
||||
class PlatformAdaptingHomePage extends StatefulWidget {
|
||||
const PlatformAdaptingHomePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_PlatformAdaptingHomePageState createState() =>
|
||||
_PlatformAdaptingHomePageState();
|
||||
@@ -107,12 +112,12 @@ class _PlatformAdaptingHomePageState extends State<PlatformAdaptingHomePage> {
|
||||
case 1:
|
||||
return CupertinoTabView(
|
||||
defaultTitle: NewsTab.title,
|
||||
builder: (context) => NewsTab(),
|
||||
builder: (context) => const NewsTab(),
|
||||
);
|
||||
case 2:
|
||||
return CupertinoTabView(
|
||||
defaultTitle: ProfileTab.title,
|
||||
builder: (context) => ProfileTab(),
|
||||
builder: (context) => const ProfileTab(),
|
||||
);
|
||||
default:
|
||||
assert(false, 'Unexpected tab');
|
||||
@@ -161,8 +166,8 @@ class _AndroidDrawer extends StatelessWidget {
|
||||
title: const Text(NewsTab.title),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.push<void>(
|
||||
context, MaterialPageRoute(builder: (context) => NewsTab()));
|
||||
Navigator.push<void>(context,
|
||||
MaterialPageRoute(builder: (context) => const NewsTab()));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
@@ -171,7 +176,7 @@ class _AndroidDrawer extends StatelessWidget {
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.push<void>(context,
|
||||
MaterialPageRoute(builder: (context) => ProfileTab()));
|
||||
MaterialPageRoute(builder: (context) => const ProfileTab()));
|
||||
},
|
||||
),
|
||||
// Long drawer contents are often segmented.
|
||||
@@ -185,7 +190,7 @@ class _AndroidDrawer extends StatelessWidget {
|
||||
onTap: () {
|
||||
Navigator.pop(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 iosIcon = Icon(CupertinoIcons.news);
|
||||
|
||||
const NewsTab({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_NewsTabState createState() => _NewsTabState();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ class ProfileTab extends StatelessWidget {
|
||||
static const androidIcon = Icon(Icons.person);
|
||||
static const iosIcon = Icon(CupertinoIcons.profile_circled);
|
||||
|
||||
const ProfileTab({Key? key}) : super(key: key);
|
||||
|
||||
Widget _buildBody(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
@@ -55,7 +57,7 @@ class ProfileTab extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Container(),
|
||||
),
|
||||
LogOutButton(),
|
||||
const LogOutButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -89,7 +91,7 @@ class ProfileTab extends StatelessWidget {
|
||||
CupertinoPageRoute(
|
||||
title: SettingsTab.title,
|
||||
fullscreenDialog: true,
|
||||
builder: (context) => SettingsTab(),
|
||||
builder: (context) => const SettingsTab(),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -113,7 +115,8 @@ class PreferenceCard extends StatelessWidget {
|
||||
required this.header,
|
||||
required this.content,
|
||||
required this.preferenceChoices,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String header;
|
||||
final String content;
|
||||
@@ -171,6 +174,8 @@ class LogOutButton extends StatelessWidget {
|
||||
static const _logoutMessage = Text(
|
||||
"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
|
||||
// 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 iosIcon = Icon(CupertinoIcons.gear);
|
||||
|
||||
const SettingsTab({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SettingsTabState createState() => _SettingsTabState();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ class SongDetailTab extends StatelessWidget {
|
||||
required this.id,
|
||||
required this.song,
|
||||
required this.color,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final int id;
|
||||
final String song;
|
||||
@@ -71,7 +72,7 @@ class SongDetailTab extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
// 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.flattenAnimation,
|
||||
this.child,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final VoidCallback? onPressed;
|
||||
final Color color;
|
||||
@@ -140,7 +141,8 @@ class HeroAnimatingSongCard extends StatelessWidget {
|
||||
required this.color,
|
||||
required this.heroAnimation,
|
||||
this.onPressed,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final String song;
|
||||
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
|
||||
/// use on both iOS and Android as part of their brand's unique design.
|
||||
class SongPlaceholderTile extends StatelessWidget {
|
||||
const SongPlaceholderTile({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'package:platform_design/main.dart';
|
||||
|
||||
void main() {
|
||||
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.
|
||||
expect(find.byIcon(Icons.menu), findsOneWidget);
|
||||
@@ -19,7 +19,7 @@ void main() {
|
||||
expect(find.byIcon(Icons.refresh), findsOneWidget);
|
||||
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||
await tester.pumpWidget(MyAdaptingApp());
|
||||
await tester.pumpWidget(const MyAdaptingApp());
|
||||
|
||||
// There should now be a large title style nav bar.
|
||||
expect(find.byType(CupertinoSliverNavigationBar), findsOneWidget);
|
||||
|
||||
Reference in New Issue
Block a user