Added ThemeMode support to the Flutter Gallery (#36399)
Made the Flutter Gallery use the new ThemeMode property so that it can
use the system's dark/light mode setting by default.
diff --git a/examples/flutter_gallery/lib/gallery/app.dart b/examples/flutter_gallery/lib/gallery/app.dart
index a502087..b21f77f 100644
--- a/examples/flutter_gallery/lib/gallery/app.dart
+++ b/examples/flutter_gallery/lib/gallery/app.dart
@@ -62,7 +62,7 @@
void initState() {
super.initState();
_options = GalleryOptions(
- theme: kLightGalleryTheme,
+ themeMode: ThemeMode.system,
textScaleFactor: kAllGalleryTextScaleValues[0],
timeDilation: timeDilation,
platform: defaultTargetPlatform,
@@ -134,7 +134,9 @@
return ScopedModel<AppStateModel>(
model: model,
child: MaterialApp(
- theme: _options.theme.data.copyWith(platform: _options.platform),
+ theme: kLightGalleryTheme.copyWith(platform: _options.platform),
+ darkTheme: kDarkGalleryTheme.copyWith(platform: _options.platform),
+ themeMode: _options.themeMode,
title: 'Flutter Gallery',
color: Colors.grey,
showPerformanceOverlay: _options.showPerformanceOverlay,
@@ -148,12 +150,14 @@
// Specifically use a blank Cupertino theme here and do not transfer
// over the Material primary color etc except the brightness to
// showcase standard iOS looks.
- CupertinoTheme(
- data: CupertinoThemeData(
- brightness: _options.theme.data.brightness,
- ),
- child: child,
- ),
+ Builder(builder: (BuildContext context) {
+ return CupertinoTheme(
+ data: CupertinoThemeData(
+ brightness: Theme.of(context).brightness,
+ ),
+ child: child,
+ );
+ }),
),
);
},
diff --git a/examples/flutter_gallery/lib/gallery/options.dart b/examples/flutter_gallery/lib/gallery/options.dart
index c5642be..6076d01 100644
--- a/examples/flutter_gallery/lib/gallery/options.dart
+++ b/examples/flutter_gallery/lib/gallery/options.dart
@@ -6,11 +6,10 @@
import 'about.dart';
import 'scales.dart';
-import 'themes.dart';
class GalleryOptions {
GalleryOptions({
- this.theme,
+ this.themeMode,
this.textScaleFactor,
this.textDirection = TextDirection.ltr,
this.timeDilation = 1.0,
@@ -20,7 +19,7 @@
this.showPerformanceOverlay = false,
});
- final GalleryTheme theme;
+ final ThemeMode themeMode;
final GalleryTextScaleValue textScaleFactor;
final TextDirection textDirection;
final double timeDilation;
@@ -30,7 +29,7 @@
final bool showOffscreenLayersCheckerboard;
GalleryOptions copyWith({
- GalleryTheme theme,
+ ThemeMode themeMode,
GalleryTextScaleValue textScaleFactor,
TextDirection textDirection,
double timeDilation,
@@ -40,7 +39,7 @@
bool showOffscreenLayersCheckerboard,
}) {
return GalleryOptions(
- theme: theme ?? this.theme,
+ themeMode: themeMode ?? this.themeMode,
textScaleFactor: textScaleFactor ?? this.textScaleFactor,
textDirection: textDirection ?? this.textDirection,
timeDilation: timeDilation ?? this.timeDilation,
@@ -56,7 +55,7 @@
if (runtimeType != other.runtimeType)
return false;
final GalleryOptions typedOther = other;
- return theme == typedOther.theme
+ return themeMode == typedOther.themeMode
&& textScaleFactor == typedOther.textScaleFactor
&& textDirection == typedOther.textDirection
&& platform == typedOther.platform
@@ -67,7 +66,7 @@
@override
int get hashCode => hashValues(
- theme,
+ themeMode,
textScaleFactor,
textDirection,
timeDilation,
@@ -79,7 +78,7 @@
@override
String toString() {
- return '$runtimeType($theme)';
+ return '$runtimeType($themeMode)';
}
}
@@ -202,25 +201,55 @@
}
}
-class _ThemeItem extends StatelessWidget {
- const _ThemeItem(this.options, this.onOptionsChanged);
+class _ThemeModeItem extends StatelessWidget {
+ const _ThemeModeItem(this.options, this.onOptionsChanged);
final GalleryOptions options;
final ValueChanged<GalleryOptions> onOptionsChanged;
+ static final Map<ThemeMode, String> modeLabels = <ThemeMode, String>{
+ ThemeMode.system: 'System Default',
+ ThemeMode.light: 'Light',
+ ThemeMode.dark: 'Dark',
+ };
+
@override
Widget build(BuildContext context) {
- return _BooleanItem(
- 'Dark Theme',
- options.theme == kDarkGalleryTheme,
- (bool value) {
- onOptionsChanged(
- options.copyWith(
- theme: value ? kDarkGalleryTheme : kLightGalleryTheme,
+ return _OptionsItem(
+ child: Row(
+ children: <Widget>[
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: <Widget>[
+ const Text('Theme'),
+ Text(
+ '${modeLabels[options.themeMode]}',
+ style: Theme.of(context).primaryTextTheme.body1,
+ ),
+ ],
+ ),
),
- );
- },
- switchKey: const Key('dark_theme'),
+ PopupMenuButton<ThemeMode>(
+ padding: const EdgeInsetsDirectional.only(end: 16.0),
+ icon: const Icon(Icons.arrow_drop_down),
+ initialValue: options.themeMode,
+ itemBuilder: (BuildContext context) {
+ return ThemeMode.values.map<PopupMenuItem<ThemeMode>>((ThemeMode mode) {
+ return PopupMenuItem<ThemeMode>(
+ value: mode,
+ child: Text(modeLabels[mode]),
+ );
+ }).toList();
+ },
+ onSelected: (ThemeMode mode) {
+ onOptionsChanged(
+ options.copyWith(themeMode: mode),
+ );
+ },
+ ),
+ ],
+ ),
);
}
}
@@ -448,7 +477,7 @@
padding: const EdgeInsets.only(bottom: 124.0),
children: <Widget>[
const _Heading('Display'),
- _ThemeItem(options, onOptionsChanged),
+ _ThemeModeItem(options, onOptionsChanged),
_TextScaleFactorItem(options, onOptionsChanged),
_TextDirectionItem(options, onOptionsChanged),
_TimeDilationItem(options, onOptionsChanged),
diff --git a/examples/flutter_gallery/lib/gallery/themes.dart b/examples/flutter_gallery/lib/gallery/themes.dart
index e500f95..7843012 100644
--- a/examples/flutter_gallery/lib/gallery/themes.dart
+++ b/examples/flutter_gallery/lib/gallery/themes.dart
@@ -4,15 +4,8 @@
import 'package:flutter/material.dart';
-class GalleryTheme {
- const GalleryTheme._(this.name, this.data);
-
- final String name;
- final ThemeData data;
-}
-
-final GalleryTheme kDarkGalleryTheme = GalleryTheme._('Dark', _buildDarkTheme());
-final GalleryTheme kLightGalleryTheme = GalleryTheme._('Light', _buildLightTheme());
+final ThemeData kLightGalleryTheme = _buildLightTheme();
+final ThemeData kDarkGalleryTheme = _buildDarkTheme();
TextTheme _buildTextTheme(TextTheme base) {
return base.copyWith(
diff --git a/examples/flutter_gallery/test/accessibility_test.dart b/examples/flutter_gallery/test/accessibility_test.dart
index 9fef8bc..2091e8c 100644
--- a/examples/flutter_gallery/test/accessibility_test.dart
+++ b/examples/flutter_gallery/test/accessibility_test.dart
@@ -472,15 +472,15 @@
group('All material demos meet text contrast guidelines', () {
final List<ThemeData> themes = <ThemeData>[
- kLightGalleryTheme.data,
+ kLightGalleryTheme,
ThemeData.light(),
- // TODO(hansmuller): add kDarkGalleryTheme.data, ThemeData.dark(), see #22044
+ // TODO(hansmuller): add kDarkGalleryTheme, ThemeData.dark(), see #22044
];
const List<String> themeNames = <String>[
'kLightGalleryTheme',
'ThemeData.light()',
- // TODO(hansmuller): add 'kDarkGalleryTheme', 'ThemeData.dark()', see 22044
+ // TODO(hansmuller): add 'kDarkGalleryTheme', 'ThemeData.dark()', see #22044
];
for (int themeIndex = 0; themeIndex < themes.length; themeIndex += 1) {
diff --git a/examples/flutter_gallery/test/drawer_test.dart b/examples/flutter_gallery/test/drawer_test.dart
index afe4bdc..5a9a614 100644
--- a/examples/flutter_gallery/test/drawer_test.dart
+++ b/examples/flutter_gallery/test/drawer_test.dart
@@ -30,18 +30,40 @@
await tester.tap(find.byTooltip('Toggle options page'));
await tester.pumpAndSettle();
+ // Verify theme settings
MaterialApp app = find.byType(MaterialApp).evaluate().first.widget;
expect(app.theme.brightness, equals(Brightness.light));
+ expect(app.darkTheme.brightness, equals(Brightness.dark));
- // Switch to the dark theme: first switch control
- await tester.tap(find.byType(Switch).first);
+ // Switch to the dark theme: first menu button, choose 'Dark'
+ await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('Dark'));
await tester.pumpAndSettle();
app = find.byType(MaterialApp).evaluate().first.widget;
- expect(app.theme.brightness, equals(Brightness.dark));
+ expect(app.themeMode, ThemeMode.dark);
+
+ // Switch to the light theme: first menu button, choose 'Light'
+ await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('Light'));
+ await tester.pumpAndSettle();
+ app = find.byType(MaterialApp).evaluate().first.widget;
+ expect(app.themeMode, ThemeMode.light);
+
+ // Switch back to system theme setting: first menu button, choose 'System Default'
+ await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('System Default').at(1));
+ await tester.pumpAndSettle();
+ app = find.byType(MaterialApp).evaluate().first.widget;
+ expect(app.themeMode, ThemeMode.system);
+
+ // Verify platform settings
expect(app.theme.platform, equals(TargetPlatform.android));
- // Popup the platform menu: second menu button, choose 'Cupertino'
- await tester.tap(find.byIcon(Icons.arrow_drop_down).at(1));
+ // Popup the platform menu: third menu button, choose 'Cupertino'
+ await tester.tap(find.byIcon(Icons.arrow_drop_down).at(2));
await tester.pumpAndSettle();
await tester.tap(find.text('Cupertino').at(1));
await tester.pumpAndSettle();
@@ -52,8 +74,8 @@
final Size origTextSize = tester.getSize(find.text('Text size'));
expect(origTextSize, equals(const Size(144.0, 16.0)));
- // Popup the text size menu: first menu button, choose 'Small'
- await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
+ // Popup the text size menu: second menu button, choose 'Small'
+ await tester.tap(find.byIcon(Icons.arrow_drop_down).at(1));
await tester.pumpAndSettle();
await tester.tap(find.text('Small'));
await tester.pumpAndSettle();
@@ -61,21 +83,21 @@
expect(textSize, equals(const Size(116.0, 13.0)));
// Set font scale back to the default.
- await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
+ await tester.tap(find.byIcon(Icons.arrow_drop_down).at(1));
await tester.pumpAndSettle();
- await tester.tap(find.text('System Default'));
+ await tester.tap(find.text('System Default').at(1));
await tester.pumpAndSettle();
textSize = tester.getSize(find.text('Text size'));
expect(textSize, origTextSize);
- // Switch to slow animation: third switch control
+ // Switch to slow animation: second switch control
expect(timeDilation, 1.0);
- await tester.tap(find.byType(Switch).at(2));
+ await tester.tap(find.byType(Switch).at(1));
await tester.pumpAndSettle();
expect(timeDilation, greaterThan(1.0));
- // Restore normal animation: third switch control
- await tester.tap(find.byType(Switch).at(2));
+ // Restore normal animation: second switch control
+ await tester.tap(find.byType(Switch).at(1));
await tester.pumpAndSettle();
expect(timeDilation, 1.0);
diff --git a/examples/flutter_gallery/test/smoke_test.dart b/examples/flutter_gallery/test/smoke_test.dart
index 9a15506..76a1dfb 100644
--- a/examples/flutter_gallery/test/smoke_test.dart
+++ b/examples/flutter_gallery/test/smoke_test.dart
@@ -109,28 +109,28 @@
await tester.tap(showOptionsPageButton);
await tester.pumpAndSettle();
- // Switch to the dark theme: first switch control
+ // Switch to the dark theme: first menu button, choose 'Dark'
+ await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('Dark'));
+ await tester.pumpAndSettle();
+
+ // Switch back to system theme setting: first menu button, choose 'System Default'
+ await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('System Default').at(1));
+ await tester.pumpAndSettle();
+
+ // Switch text direction: first switch
await tester.tap(find.byType(Switch).first);
await tester.pumpAndSettle();
- // Switch back to the light theme: first switch control again
+ // Switch back to system text direction: first switch control again
await tester.tap(find.byType(Switch).first);
await tester.pumpAndSettle();
- // Popup the text size menu: first menu button, choose 'Small'
- await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
- await tester.pumpAndSettle();
- await tester.tap(find.text('Small'));
- await tester.pumpAndSettle();
-
- // Popup the text size menu: first menu button, choose 'Normal'
- await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
- await tester.pumpAndSettle();
- await tester.tap(find.text('Normal'));
- await tester.pumpAndSettle();
-
// Scroll the 'Send feedback' item into view
- await tester.drag(find.text('Normal'), const Offset(0.0, -1000.0));
+ await tester.drag(find.text('Theme'), const Offset(0.0, -1000.0));
await tester.pumpAndSettle();
await tester.tap(find.text('Send feedback'));
await tester.pumpAndSettle();