From 924efae96c34cdf2f4b78f219f36419a68b0b4e8 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 20 Dec 2019 20:36:29 -0800 Subject: [PATCH] Add visual density to the gallery options (#46090) This PR adds an option to the gallery options that sets the visual density to one of "standard" (the default), "comfortable", or "compact". --- examples/flutter_gallery/lib/gallery/app.dart | 5 +- .../flutter_gallery/lib/gallery/options.dart | 53 +++++++++++++++++++ .../flutter_gallery/lib/gallery/scales.dart | 31 +++++++++++ .../flutter_gallery/test/drawer_test.dart | 35 ++++++++++-- 4 files changed, 118 insertions(+), 6 deletions(-) diff --git a/examples/flutter_gallery/lib/gallery/app.dart b/examples/flutter_gallery/lib/gallery/app.dart index 9c3b57f7dd..5afc8bd27e 100644 --- a/examples/flutter_gallery/lib/gallery/app.dart +++ b/examples/flutter_gallery/lib/gallery/app.dart @@ -64,6 +64,7 @@ class _GalleryAppState extends State { _options = GalleryOptions( themeMode: ThemeMode.system, textScaleFactor: kAllGalleryTextScaleValues[0], + visualDensity: kAllGalleryVisualDensityValues[0], timeDilation: timeDilation, platform: defaultTargetPlatform, ); @@ -140,8 +141,8 @@ class _GalleryAppState extends State { return ScopedModel( model: model, child: MaterialApp( - theme: kLightGalleryTheme.copyWith(platform: _options.platform), - darkTheme: kDarkGalleryTheme.copyWith(platform: _options.platform), + theme: kLightGalleryTheme.copyWith(platform: _options.platform, visualDensity: _options.visualDensity.visualDensity), + darkTheme: kDarkGalleryTheme.copyWith(platform: _options.platform, visualDensity: _options.visualDensity.visualDensity), themeMode: _options.themeMode, title: 'Flutter Gallery', color: Colors.grey, diff --git a/examples/flutter_gallery/lib/gallery/options.dart b/examples/flutter_gallery/lib/gallery/options.dart index e94eb6de2a..841090eaee 100644 --- a/examples/flutter_gallery/lib/gallery/options.dart +++ b/examples/flutter_gallery/lib/gallery/options.dart @@ -11,6 +11,7 @@ class GalleryOptions { GalleryOptions({ this.themeMode, this.textScaleFactor, + this.visualDensity, this.textDirection = TextDirection.ltr, this.timeDilation = 1.0, this.platform, @@ -21,6 +22,7 @@ class GalleryOptions { final ThemeMode themeMode; final GalleryTextScaleValue textScaleFactor; + final GalleryVisualDensityValue visualDensity; final TextDirection textDirection; final double timeDilation; final TargetPlatform platform; @@ -31,6 +33,7 @@ class GalleryOptions { GalleryOptions copyWith({ ThemeMode themeMode, GalleryTextScaleValue textScaleFactor, + GalleryVisualDensityValue visualDensity, TextDirection textDirection, double timeDilation, TargetPlatform platform, @@ -41,6 +44,7 @@ class GalleryOptions { return GalleryOptions( themeMode: themeMode ?? this.themeMode, textScaleFactor: textScaleFactor ?? this.textScaleFactor, + visualDensity: visualDensity ?? this.visualDensity, textDirection: textDirection ?? this.textDirection, timeDilation: timeDilation ?? this.timeDilation, platform: platform ?? this.platform, @@ -57,6 +61,7 @@ class GalleryOptions { return other is GalleryOptions && other.themeMode == themeMode && other.textScaleFactor == textScaleFactor + && other.visualDensity == visualDensity && other.textDirection == textDirection && other.platform == platform && other.showPerformanceOverlay == showPerformanceOverlay @@ -68,6 +73,7 @@ class GalleryOptions { int get hashCode => hashValues( themeMode, textScaleFactor, + visualDensity, textDirection, timeDilation, platform, @@ -300,6 +306,52 @@ class _TextScaleFactorItem extends StatelessWidget { } } +class _VisualDensityItem extends StatelessWidget { + const _VisualDensityItem(this.options, this.onOptionsChanged); + + final GalleryOptions options; + final ValueChanged onOptionsChanged; + + @override + Widget build(BuildContext context) { + return _OptionsItem( + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Visual density'), + Text( + '${options.visualDensity.label}', + style: Theme.of(context).primaryTextTheme.body1, + ), + ], + ), + ), + PopupMenuButton( + padding: const EdgeInsetsDirectional.only(end: 16.0), + icon: const Icon(Icons.arrow_drop_down), + itemBuilder: (BuildContext context) { + return kAllGalleryVisualDensityValues.map>((GalleryVisualDensityValue densityValue) { + return PopupMenuItem( + value: densityValue, + child: Text(densityValue.label), + ); + }).toList(); + }, + onSelected: (GalleryVisualDensityValue densityValue) { + onOptionsChanged( + options.copyWith(visualDensity: densityValue), + ); + }, + ), + ], + ), + ); + } +} + class _TextDirectionItem extends StatelessWidget { const _TextDirectionItem(this.options, this.onOptionsChanged); @@ -469,6 +521,7 @@ class GalleryOptionsPage extends StatelessWidget { const _Heading('Display'), _ThemeModeItem(options, onOptionsChanged), _TextScaleFactorItem(options, onOptionsChanged), + _VisualDensityItem(options, onOptionsChanged), _TextDirectionItem(options, onOptionsChanged), _TimeDilationItem(options, onOptionsChanged), const Divider(), diff --git a/examples/flutter_gallery/lib/gallery/scales.dart b/examples/flutter_gallery/lib/gallery/scales.dart index 36f20a216a..55ad8863e3 100644 --- a/examples/flutter_gallery/lib/gallery/scales.dart +++ b/examples/flutter_gallery/lib/gallery/scales.dart @@ -36,3 +36,34 @@ const List kAllGalleryTextScaleValues = hashValues(visualDensity, label); + + @override + String toString() { + return '$runtimeType($label)'; + } + +} + +const List kAllGalleryVisualDensityValues = [ + GalleryVisualDensityValue(VisualDensity(), 'System Default'), + GalleryVisualDensityValue(VisualDensity.comfortable, 'Comfortable'), + GalleryVisualDensityValue(VisualDensity.compact, 'Compact'), + GalleryVisualDensityValue(VisualDensity(horizontal: -3, vertical: -3), 'Very Compact'), +]; diff --git a/examples/flutter_gallery/test/drawer_test.dart b/examples/flutter_gallery/test/drawer_test.dart index ea7b592289..585f10ebd7 100644 --- a/examples/flutter_gallery/test/drawer_test.dart +++ b/examples/flutter_gallery/test/drawer_test.dart @@ -54,16 +54,40 @@ void main() { // 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.tap(find.descendant( + of: find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == 'PopupMenuItem'), + matching: find.text('System Default') + )); await tester.pumpAndSettle(); app = find.byType(MaterialApp).evaluate().first.widget as MaterialApp; expect(app.themeMode, ThemeMode.system); + // Verify density settings + expect(app.theme.visualDensity, equals(const VisualDensity())); + + // Popup the density menu: third menu button, choose 'Compact' + await tester.tap(find.byIcon(Icons.arrow_drop_down).at(2)); + await tester.pumpAndSettle(); + await tester.tap(find.text('Compact')); + await tester.pumpAndSettle(); + app = find.byType(MaterialApp).evaluate().first.widget; + expect(app.theme.visualDensity, equals(VisualDensity.compact)); + + await tester.tap(find.byIcon(Icons.arrow_drop_down).at(2)); + await tester.pumpAndSettle(); + await tester.tap(find.descendant( + of: find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == 'PopupMenuItem'), + matching: find.text('System Default') + )); + await tester.pumpAndSettle(); + app = find.byType(MaterialApp).evaluate().first.widget; + expect(app.theme.visualDensity, equals(const VisualDensity())); + // Verify platform settings expect(app.theme.platform, equals(TargetPlatform.android)); - // Popup the platform menu: third menu button, choose 'Cupertino' - await tester.tap(find.byIcon(Icons.arrow_drop_down).at(2)); + // Popup the platform menu: fourth menu button, choose 'Cupertino' + await tester.tap(find.byIcon(Icons.arrow_drop_down).at(3)); await tester.pumpAndSettle(); await tester.tap(find.text('Cupertino').at(1)); await tester.pumpAndSettle(); @@ -85,7 +109,10 @@ void main() { // Set font scale back to the default. await tester.tap(find.byIcon(Icons.arrow_drop_down).at(1)); await tester.pumpAndSettle(); - await tester.tap(find.text('System Default').at(1)); + await tester.tap(find.descendant( + of: find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == 'PopupMenuItem'), + matching: find.text('System Default') + )); await tester.pumpAndSettle(); textSize = tester.getSize(find.text('Text size')); expect(textSize, origTextSize);