Unverified Commit b5fff3b2 authored by Darren Austin's avatar Darren Austin Committed by GitHub

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.
parent 11d3d45b
......@@ -62,7 +62,7 @@ class _GalleryAppState extends State<GalleryApp> {
void initState() {
super.initState();
_options = GalleryOptions(
theme: kLightGalleryTheme,
themeMode: ThemeMode.system,
textScaleFactor: kAllGalleryTextScaleValues[0],
timeDilation: timeDilation,
platform: defaultTargetPlatform,
......@@ -134,7 +134,9 @@ class _GalleryAppState extends State<GalleryApp> {
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 @@ class _GalleryAppState extends State<GalleryApp> {
// 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,
);
}),
),
);
},
......
......@@ -6,11 +6,10 @@ import 'package:flutter/material.dart';
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 @@ class GalleryOptions {
this.showPerformanceOverlay = false,
});
final GalleryTheme theme;
final ThemeMode themeMode;
final GalleryTextScaleValue textScaleFactor;
final TextDirection textDirection;
final double timeDilation;
......@@ -30,7 +29,7 @@ class GalleryOptions {
final bool showOffscreenLayersCheckerboard;
GalleryOptions copyWith({
GalleryTheme theme,
ThemeMode themeMode,
GalleryTextScaleValue textScaleFactor,
TextDirection textDirection,
double timeDilation,
......@@ -40,7 +39,7 @@ class GalleryOptions {
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 @@ class GalleryOptions {
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 @@ class GalleryOptions {
@override
int get hashCode => hashValues(
theme,
themeMode,
textScaleFactor,
textDirection,
timeDilation,
......@@ -79,7 +78,7 @@ class GalleryOptions {
@override
String toString() {
return '$runtimeType($theme)';
return '$runtimeType($themeMode)';
}
}
......@@ -202,25 +201,55 @@ class _Heading extends StatelessWidget {
}
}
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 @@ class GalleryOptionsPage extends StatelessWidget {
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),
......
......@@ -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(
......
......@@ -472,15 +472,15 @@ void main() {
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) {
......
......@@ -30,18 +30,40 @@ void main() {
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.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.theme.brightness, equals(Brightness.dark));
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 @@ void main() {
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 @@ void main() {
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);
......
......@@ -109,28 +109,28 @@ Future<void> smokeOptionsPage(WidgetTester tester) async {
await tester.tap(showOptionsPageButton);
await tester.pumpAndSettle();
// 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();
// Switch back to the light theme: first switch control again
await tester.tap(find.byType(Switch).first);
await tester.tap(find.text('Dark'));
await tester.pumpAndSettle();
// Popup the text size menu: first menu button, choose 'Small'
// 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('Small'));
await tester.tap(find.text('System Default').at(1));
await tester.pumpAndSettle();
// Popup the text size menu: first menu button, choose 'Normal'
await tester.tap(find.byIcon(Icons.arrow_drop_down).first);
// Switch text direction: first switch
await tester.tap(find.byType(Switch).first);
await tester.pumpAndSettle();
await tester.tap(find.text('Normal'));
// Switch back to system text direction: first switch control again
await tester.tap(find.byType(Switch).first);
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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment