diff --git a/gallery/gallery/lib/main.dart b/gallery/gallery/lib/main.dart index d8c0e1de5..b0326591a 100644 --- a/gallery/gallery/lib/main.dart +++ b/gallery/gallery/lib/main.dart @@ -12,8 +12,6 @@ import 'package:gallery/constants.dart'; import 'package:gallery/data/gallery_options.dart'; import 'package:gallery/l10n/gallery_localizations.dart'; import 'package:gallery/pages/backdrop.dart'; -import 'package:gallery/pages/home.dart'; -import 'package:gallery/pages/settings.dart'; import 'package:gallery/pages/splash.dart'; import 'package:gallery/themes/gallery_theme_data.dart'; @@ -70,10 +68,7 @@ class GalleryApp extends StatelessWidget { }, home: ApplyTextOptions( child: SplashPage( - child: Backdrop( - frontLayer: SettingsPage(), - backLayer: HomePage(), - ), + child: AnimatedBackdrop(), ), ), ); diff --git a/gallery/gallery/lib/pages/backdrop.dart b/gallery/gallery/lib/pages/backdrop.dart index a09b4f0e8..7bf307c52 100644 --- a/gallery/gallery/lib/pages/backdrop.dart +++ b/gallery/gallery/lib/pages/backdrop.dart @@ -13,17 +13,93 @@ import 'package:gallery/constants.dart'; import 'package:gallery/data/gallery_options.dart'; import 'package:gallery/l10n/gallery_localizations.dart'; import 'package:gallery/layout/adaptive.dart'; +import 'package:gallery/pages/home.dart'; +import 'package:gallery/pages/settings.dart'; + +class AnimatedBackdrop extends StatefulWidget { + @override + _AnimatedBackdropState createState() => _AnimatedBackdropState(); +} + +class _AnimatedBackdropState extends State + with SingleTickerProviderStateMixin { + AnimationController backdropController; + ValueNotifier isSettingsOpenNotifier; + Animation openSettingsAnimation; + Animation staggerSettingsItemsAnimation; + + @override + void initState() { + super.initState(); + backdropController = AnimationController( + duration: Duration(milliseconds: 200), + vsync: this, + )..addListener(() { + setState(() { + // The state that has changed here is the animation. + }); + }); + isSettingsOpenNotifier = ValueNotifier(false); + openSettingsAnimation = CurvedAnimation( + parent: backdropController, + curve: Interval( + 0.0, + 0.4, + curve: Curves.ease, + ), + ); + staggerSettingsItemsAnimation = CurvedAnimation( + parent: backdropController, + curve: Interval( + 0.5, + 1.0, + curve: Curves.easeIn, + ), + ); + } + + @override + void dispose() { + backdropController.dispose(); + isSettingsOpenNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Backdrop( + controller: backdropController, + isSettingsOpenNotifier: isSettingsOpenNotifier, + openSettingsAnimation: openSettingsAnimation, + frontLayer: SettingsPage( + openSettingsAnimation: openSettingsAnimation, + staggerSettingsItemsAnimation: staggerSettingsItemsAnimation, + isSettingsOpenNotifier: isSettingsOpenNotifier, + ), + backLayer: HomePage(), + ); + } +} class Backdrop extends StatefulWidget { final Widget frontLayer; final Widget backLayer; + final AnimationController controller; + final Animation openSettingsAnimation; + final ValueNotifier isSettingsOpenNotifier; Backdrop({ Key key, @required this.frontLayer, @required this.backLayer, + @required this.controller, + @required this.openSettingsAnimation, + @required this.isSettingsOpenNotifier, }) : assert(frontLayer != null), assert(backLayer != null), + assert(controller != null), + assert(isSettingsOpenNotifier != null), + assert(openSettingsAnimation != null), super(key: key); @override @@ -32,8 +108,6 @@ class Backdrop extends StatefulWidget { class _BackdropState extends State with SingleTickerProviderStateMixin, FlareController { - AnimationController _controller; - Animation _animationReversed; FlareAnimationLayer _animationLayer; FlutterActorArtboard _artboard; @@ -41,7 +115,6 @@ class _BackdropState extends State double settingsButtonHeightDesktop = 56; double settingsButtonHeightMobile = 40; - bool _isSettingsOpen; FocusNode frontLayerFocusNode; FocusNode backLayerFocusNode; @@ -50,20 +123,10 @@ class _BackdropState extends State super.initState(); frontLayerFocusNode = FocusNode(); backLayerFocusNode = FocusNode(); - - _isSettingsOpen = false; - _controller = AnimationController( - duration: Duration(milliseconds: 100), value: 1, vsync: this) - ..addListener(() { - this.setState(() {}); - }); - _animationReversed = - Tween(begin: 1.0, end: 0.0).animate(_controller); } @override void dispose() { - _controller.dispose(); frontLayerFocusNode.dispose(); backLayerFocusNode.dispose(); super.dispose(); @@ -84,7 +147,7 @@ class _BackdropState extends State bool advance(FlutterActorArtboard artboard, double elapsed) { if (_animationLayer != null) { FlareAnimationLayer layer = _animationLayer; - layer.time = _animationReversed.value * layer.duration; + layer.time = widget.controller.value * layer.duration; layer.animation.apply(layer.time, _artboard, 1); if (layer.isDone || layer.time == 0) { _animationLayer = null; @@ -104,10 +167,16 @@ class _BackdropState extends State } void toggleSettings() { - _controller.fling(velocity: _isSettingsOpen ? 1 : -1); + // Animate the settings panel to open or close. + widget.controller + .fling(velocity: widget.isSettingsOpenNotifier.value ? -1 : 1); + setState(() { + widget.isSettingsOpenNotifier.value = + !widget.isSettingsOpenNotifier.value; + }); + // Animate the settings icon. initAnimationLayer(); isActive.value = true; - _isSettingsOpen = !_isSettingsOpen; } Animation _getPanelAnimation(BoxConstraints constraints) { @@ -115,14 +184,17 @@ class _BackdropState extends State final double top = height - galleryHeaderHeight; final double bottom = -galleryHeaderHeight; return RelativeRectTween( - begin: RelativeRect.fromLTRB(0, top, 0, bottom), - end: RelativeRect.fromLTRB(0, 0, 0, 0), - ).animate(CurvedAnimation(parent: _controller, curve: Curves.linear)); + begin: RelativeRect.fromLTRB(0, 0, 0, 0), + end: RelativeRect.fromLTRB(0, top, 0, bottom), + ).animate(CurvedAnimation( + parent: widget.openSettingsAnimation, + curve: Curves.linear, + )); } Widget _galleryHeader() { return ExcludeSemantics( - excluding: _isSettingsOpen, + excluding: widget.isSettingsOpenNotifier.value, child: Semantics( sortKey: OrdinalSortKey( GalleryOptions.of(context).textDirection() == TextDirection.ltr @@ -137,7 +209,6 @@ class _BackdropState extends State } Widget _buildStack(BuildContext context, BoxConstraints constraints) { - final Animation animation = _getPanelAnimation(constraints); final isDesktop = isDisplayDesktop(context); final safeAreaTopPadding = MediaQuery.of(context).padding.top; @@ -145,21 +216,15 @@ class _BackdropState extends State child: DefaultFocusTraversal( policy: WidgetOrderFocusTraversalPolicy(), child: Focus( - skipTraversal: !_isSettingsOpen, - child: InheritedBackdrop( - toggleSettings: toggleSettings, - child: widget.frontLayer, - settingsButtonWidth: settingsButtonWidth, - desktopSettingsButtonHeight: settingsButtonHeightDesktop, - mobileSettingsButtonHeight: settingsButtonHeightMobile, - ), + skipTraversal: !widget.isSettingsOpenNotifier.value, + child: widget.frontLayer, ), ), - excluding: !_isSettingsOpen, + excluding: !widget.isSettingsOpenNotifier.value, ); final Widget backLayer = ExcludeSemantics( child: widget.backLayer, - excluding: _isSettingsOpen, + excluding: widget.isSettingsOpenNotifier.value, ); return DefaultFocusTraversal( @@ -173,14 +238,14 @@ class _BackdropState extends State _galleryHeader(), frontLayer, PositionedTransition( - rect: animation, + rect: _getPanelAnimation(constraints), child: backLayer, ), ], if (isDesktop) ...[ _galleryHeader(), backLayer, - if (_isSettingsOpen) ...[ + if (widget.isSettingsOpenNotifier.value) ...[ ExcludeSemantics( child: ModalBarrier( dismissible: true, @@ -199,7 +264,9 @@ class _BackdropState extends State ? Alignment.topRight : Alignment.topLeft, scale: CurvedAnimation( - parent: _animationReversed, + parent: isDesktop + ? widget.controller + : widget.openSettingsAnimation, curve: Curves.easeIn, reverseCurve: Curves.easeOut, ), @@ -226,7 +293,7 @@ class _BackdropState extends State alignment: AlignmentDirectional.topEnd, child: Semantics( button: true, - label: _isSettingsOpen + label: widget.isSettingsOpenNotifier.value ? GalleryLocalizations.of(context) .settingsButtonCloseLabel : GalleryLocalizations.of(context).settingsButtonLabel, @@ -239,7 +306,8 @@ class _BackdropState extends State borderRadius: BorderRadiusDirectional.only( bottomStart: Radius.circular(10), ), - color: _isSettingsOpen & !_controller.isAnimating + color: widget.isSettingsOpenNotifier.value & + !widget.controller.isAnimating ? Colors.transparent : Theme.of(context).colorScheme.secondaryVariant, clipBehavior: Clip.antiAlias, @@ -252,7 +320,7 @@ class _BackdropState extends State onFocusChange: (hasFocus) { if (!hasFocus) { FocusScope.of(context).requestFocus( - (_isSettingsOpen) + (widget.isSettingsOpenNotifier.value) ? frontLayerFocusNode : backLayerFocusNode); } @@ -291,30 +359,6 @@ class _BackdropState extends State } } -class InheritedBackdrop extends InheritedWidget { - final void Function() toggleSettings; - final double settingsButtonWidth; - final double desktopSettingsButtonHeight; - final double mobileSettingsButtonHeight; - - InheritedBackdrop({ - this.toggleSettings, - this.settingsButtonWidth, - this.desktopSettingsButtonHeight, - this.mobileSettingsButtonHeight, - Widget child, - }) : super(child: child); - - @override - bool updateShouldNotify(InheritedWidget oldWidget) { - return true; - } - - static InheritedBackdrop of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType(); - } -} - class InheritedBackdropFocusNodes extends InheritedWidget { InheritedBackdropFocusNodes({ @required Widget child, diff --git a/gallery/gallery/lib/pages/settings.dart b/gallery/gallery/lib/pages/settings.dart index acae4c129..907579fe7 100644 --- a/gallery/gallery/lib/pages/settings.dart +++ b/gallery/gallery/lib/pages/settings.dart @@ -4,7 +4,7 @@ import 'dart:collection'; -import "package:collection/collection.dart"; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localized_countries/flutter_localized_countries.dart'; import 'package:gallery/constants.dart'; @@ -26,6 +26,17 @@ enum _ExpandableSetting { } class SettingsPage extends StatefulWidget { + SettingsPage({ + Key key, + @required this.openSettingsAnimation, + @required this.staggerSettingsItemsAnimation, + @required this.isSettingsOpenNotifier, + }) : super(key: key); + + final Animation openSettingsAnimation; + final Animation staggerSettingsItemsAnimation; + final ValueNotifier isSettingsOpenNotifier; + @override _SettingsPageState createState() => _SettingsPageState(); } @@ -54,6 +65,15 @@ class _SettingsPageState extends State { }, ), ); + + // When closing settings, also shrink expanded setting. + widget.isSettingsOpenNotifier.addListener(() { + if (!widget.isSettingsOpenNotifier.value) { + setState(() { + expandedSettingId = null; + }); + } + }); } /// Given a [Locale], returns a [DisplayOption] with its native name for a @@ -119,156 +139,168 @@ class _SettingsPageState extends State { final options = GalleryOptions.of(context); final isDesktop = isDisplayDesktop(context); + final settingsListItems = [ + SettingsListItem( + title: GalleryLocalizations.of(context).settingsTextScaling, + selectedOption: options.textScaleFactor( + context, + useSentinel: true, + ), + options: LinkedHashMap.of({ + systemTextScaleFactorOption: DisplayOption( + GalleryLocalizations.of(context).settingsSystemDefault, + ), + 0.8: DisplayOption( + GalleryLocalizations.of(context).settingsTextScalingSmall, + ), + 1.0: DisplayOption( + GalleryLocalizations.of(context).settingsTextScalingNormal, + ), + 2.0: DisplayOption( + GalleryLocalizations.of(context).settingsTextScalingLarge, + ), + 3.0: DisplayOption( + GalleryLocalizations.of(context).settingsTextScalingHuge, + ), + }), + onOptionChanged: (newTextScale) => GalleryOptions.update( + context, + options.copyWith(textScaleFactor: newTextScale), + ), + onTapSetting: () => onTapSetting(_ExpandableSetting.textScale), + isExpanded: expandedSettingId == _ExpandableSetting.textScale, + ), + SettingsListItem( + title: GalleryLocalizations.of(context).settingsTextDirection, + selectedOption: options.customTextDirection, + options: LinkedHashMap.of({ + CustomTextDirection.localeBased: DisplayOption( + GalleryLocalizations.of(context).settingsTextDirectionLocaleBased, + ), + CustomTextDirection.ltr: DisplayOption( + GalleryLocalizations.of(context).settingsTextDirectionLTR, + ), + CustomTextDirection.rtl: DisplayOption( + GalleryLocalizations.of(context).settingsTextDirectionRTL, + ), + }), + onOptionChanged: (newTextDirection) => GalleryOptions.update( + context, + options.copyWith(customTextDirection: newTextDirection), + ), + onTapSetting: () => onTapSetting(_ExpandableSetting.textDirection), + isExpanded: expandedSettingId == _ExpandableSetting.textDirection, + ), + SettingsListItem( + title: GalleryLocalizations.of(context).settingsLocale, + selectedOption: options.locale == deviceLocale + ? systemLocaleOption + : options.locale, + options: _getLocaleOptions(), + onOptionChanged: (newLocale) { + if (newLocale == systemLocaleOption) { + newLocale = deviceLocale; + } + GalleryOptions.update( + context, + options.copyWith(locale: newLocale), + ); + }, + onTapSetting: () => onTapSetting(_ExpandableSetting.locale), + isExpanded: expandedSettingId == _ExpandableSetting.locale, + ), + SettingsListItem( + title: GalleryLocalizations.of(context).settingsPlatformMechanics, + selectedOption: options.platform, + options: LinkedHashMap.of({ + TargetPlatform.android: DisplayOption( + GalleryLocalizations.of(context).settingsPlatformAndroid, + ), + TargetPlatform.iOS: DisplayOption( + GalleryLocalizations.of(context).settingsPlatformIOS, + ), + }), + onOptionChanged: (newPlatform) => GalleryOptions.update( + context, + options.copyWith(platform: newPlatform), + ), + onTapSetting: () => onTapSetting(_ExpandableSetting.platform), + isExpanded: expandedSettingId == _ExpandableSetting.platform, + ), + SettingsListItem( + title: GalleryLocalizations.of(context).settingsTheme, + selectedOption: options.themeMode, + options: LinkedHashMap.of({ + ThemeMode.system: DisplayOption( + GalleryLocalizations.of(context).settingsSystemDefault, + ), + ThemeMode.dark: DisplayOption( + GalleryLocalizations.of(context).settingsDarkTheme, + ), + ThemeMode.light: DisplayOption( + GalleryLocalizations.of(context).settingsLightTheme, + ), + }), + onOptionChanged: (newThemeMode) => GalleryOptions.update( + context, + options.copyWith(themeMode: newThemeMode), + ), + onTapSetting: () => onTapSetting(_ExpandableSetting.theme), + isExpanded: expandedSettingId == _ExpandableSetting.theme, + ), + SlowMotionSetting(), + ]; + return Material( color: colorScheme.secondaryVariant, - child: Padding( - padding: isDesktop - ? EdgeInsets.zero - : EdgeInsets.only(bottom: galleryHeaderHeight), - // Remove ListView top padding as it is already accounted for. - child: MediaQuery.removePadding( - removeTop: isDesktop, - context: context, - child: ListView( - children: [ - SizedBox(height: firstHeaderDesktopTopPadding), - Focus( - focusNode: - InheritedBackdropFocusNodes.of(context).frontLayerFocusNode, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 32), - child: ExcludeSemantics( - child: Header( - color: Theme.of(context).colorScheme.onSurface, - text: GalleryLocalizations.of(context).settingsTitle, - ), - ), + child: _AnimatedSettingsPage( + animation: widget.openSettingsAnimation, + child: Padding( + padding: isDesktop + ? EdgeInsets.zero + : EdgeInsets.only( + bottom: galleryHeaderHeight, ), - ), - SettingsListItem( - title: GalleryLocalizations.of(context).settingsTextScaling, - selectedOption: options.textScaleFactor( - context, - useSentinel: true, - ), - options: LinkedHashMap.of({ - systemTextScaleFactorOption: DisplayOption( - GalleryLocalizations.of(context).settingsSystemDefault, - ), - 0.8: DisplayOption( - GalleryLocalizations.of(context).settingsTextScalingSmall, - ), - 1.0: DisplayOption( - GalleryLocalizations.of(context).settingsTextScalingNormal, - ), - 2.0: DisplayOption( - GalleryLocalizations.of(context).settingsTextScalingLarge, - ), - 3.0: DisplayOption( - GalleryLocalizations.of(context).settingsTextScalingHuge, - ), - }), - onOptionChanged: (newTextScale) => GalleryOptions.update( - context, - options.copyWith(textScaleFactor: newTextScale), - ), - onTapSetting: () => onTapSetting(_ExpandableSetting.textScale), - isExpanded: expandedSettingId == _ExpandableSetting.textScale, - ), - SettingsListItem( - title: GalleryLocalizations.of(context).settingsTextDirection, - selectedOption: options.customTextDirection, - options: LinkedHashMap.of({ - CustomTextDirection.localeBased: DisplayOption( - GalleryLocalizations.of(context) - .settingsTextDirectionLocaleBased, - ), - CustomTextDirection.ltr: DisplayOption( - GalleryLocalizations.of(context).settingsTextDirectionLTR, - ), - CustomTextDirection.rtl: DisplayOption( - GalleryLocalizations.of(context).settingsTextDirectionRTL, + // Remove ListView top padding as it is already accounted for. + child: MediaQuery.removePadding( + removeTop: isDesktop, + context: context, + child: ListView( + children: [ + if (isDesktop) SizedBox(height: firstHeaderDesktopTopPadding), + Focus( + focusNode: InheritedBackdropFocusNodes.of(context) + .frontLayerFocusNode, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 32), + child: ExcludeSemantics( + child: Header( + color: Theme.of(context).colorScheme.onSurface, + text: GalleryLocalizations.of(context).settingsTitle, + ), + ), ), - }), - onOptionChanged: (newTextDirection) => GalleryOptions.update( - context, - options.copyWith(customTextDirection: newTextDirection), ), - onTapSetting: () => - onTapSetting(_ExpandableSetting.textDirection), - isExpanded: - expandedSettingId == _ExpandableSetting.textDirection, - ), - SettingsListItem( - title: GalleryLocalizations.of(context).settingsLocale, - selectedOption: options.locale == deviceLocale - ? systemLocaleOption - : options.locale, - options: _getLocaleOptions(), - onOptionChanged: (newLocale) { - if (newLocale == systemLocaleOption) { - newLocale = deviceLocale; - } - GalleryOptions.update( - context, - options.copyWith(locale: newLocale), - ); - }, - onTapSetting: () => onTapSetting(_ExpandableSetting.locale), - isExpanded: expandedSettingId == _ExpandableSetting.locale, - ), - SettingsListItem( - title: - GalleryLocalizations.of(context).settingsPlatformMechanics, - selectedOption: options.platform, - options: LinkedHashMap.of({ - TargetPlatform.android: DisplayOption( - GalleryLocalizations.of(context).settingsPlatformAndroid, + if (isDesktop) + ...settingsListItems + else ...[ + _AnimateSettingsListItems( + animation: widget.staggerSettingsItemsAnimation, + children: settingsListItems, ), - TargetPlatform.iOS: DisplayOption( - GalleryLocalizations.of(context).settingsPlatformIOS, - ), - }), - onOptionChanged: (newPlatform) => GalleryOptions.update( - context, - options.copyWith(platform: newPlatform), - ), - onTapSetting: () => onTapSetting(_ExpandableSetting.platform), - isExpanded: expandedSettingId == _ExpandableSetting.platform, - ), - SettingsListItem( - title: GalleryLocalizations.of(context).settingsTheme, - selectedOption: options.themeMode, - options: LinkedHashMap.of({ - ThemeMode.system: DisplayOption( - GalleryLocalizations.of(context).settingsSystemDefault, - ), - ThemeMode.dark: DisplayOption( - GalleryLocalizations.of(context).settingsDarkTheme, - ), - ThemeMode.light: DisplayOption( - GalleryLocalizations.of(context).settingsLightTheme, - ), - }), - onOptionChanged: (newThemeMode) => GalleryOptions.update( - context, - options.copyWith(themeMode: newThemeMode), - ), - onTapSetting: () => onTapSetting(_ExpandableSetting.theme), - isExpanded: expandedSettingId == _ExpandableSetting.theme, - ), - SlowMotionSetting(), - if (!isDesktop) ...[ - SizedBox(height: 16), - Divider(thickness: 2, height: 0, color: colorScheme.background), - SizedBox(height: 12), - SettingsAbout(), - SettingsFeedback(), - SizedBox(height: 12), - Divider(thickness: 2, height: 0, color: colorScheme.background), - SettingsAttribution(), + SizedBox(height: 16), + Divider( + thickness: 2, height: 0, color: colorScheme.background), + SizedBox(height: 12), + SettingsAbout(), + SettingsFeedback(), + SizedBox(height: 12), + Divider( + thickness: 2, height: 0, color: colorScheme.background), + SettingsAttribution(), + ], ], - ], + ), ), ), ), @@ -383,3 +415,93 @@ class _SettingsLink extends StatelessWidget { ); } } + +/// Animate the settings page to slide in from above. +class _AnimatedSettingsPage extends StatelessWidget { + const _AnimatedSettingsPage({ + Key key, + @required this.animation, + @required this.child, + }) : super(key: key); + + final Widget child; + final Animation animation; + + @override + Widget build(BuildContext context) { + final isDesktop = isDisplayDesktop(context); + + if (isDesktop) { + return child; + } else { + return LayoutBuilder(builder: (context, constraints) { + return Stack( + children: [ + PositionedTransition( + rect: RelativeRectTween( + begin: RelativeRect.fromLTRB(0, -constraints.maxHeight, 0, 0), + end: RelativeRect.fromLTRB(0, 0, 0, 0), + ).animate( + CurvedAnimation( + parent: animation, + curve: Curves.linear, + ), + ), + child: child, + ), + ], + ); + }); + } + } +} + +/// Animate the settings list items to stagger in from above. +class _AnimateSettingsListItems extends StatelessWidget { + const _AnimateSettingsListItems({ + Key key, + this.animation, + this.children, + this.topPadding, + this.bottomPadding, + }) : super(key: key); + + final Animation animation; + final List children; + final double topPadding; + final double bottomPadding; + + @override + Widget build(BuildContext context) { + final startDividingPadding = 4.0; + final topPaddingTween = Tween( + begin: 0, + end: children.length * startDividingPadding, + ); + final dividerTween = Tween( + begin: startDividingPadding, + end: 0, + ); + + return Padding( + padding: EdgeInsets.only(top: topPaddingTween.animate(animation).value), + child: Column( + children: [ + for (Widget child in children) + AnimatedBuilder( + animation: animation, + builder: (context, child) { + return Padding( + padding: EdgeInsets.only( + top: dividerTween.animate(animation).value, + ), + child: child, + ); + }, + child: child, + ), + ], + ), + ); + } +} diff --git a/gallery/gallery/test/pages/home_test.dart b/gallery/gallery/test/pages/home_test.dart index f7a4df129..01cf6eb64 100644 --- a/gallery/gallery/test/pages/home_test.dart +++ b/gallery/gallery/test/pages/home_test.dart @@ -10,6 +10,11 @@ import 'package:gallery/pages/backdrop.dart'; void main() { testWidgets('Home page hides settings semantics when closed', (tester) async { + final animationController = AnimationController( + duration: Duration(milliseconds: 1), + vsync: const TestVSync(), + ); + final isSettingsOpen = ValueNotifier(false); await tester.pumpWidget( MaterialApp( localizationsDelegates: [GalleryLocalizations.delegate], @@ -20,6 +25,9 @@ void main() { child: Backdrop( frontLayer: Text('Front'), backLayer: Text('Back'), + controller: animationController, + isSettingsOpenNotifier: isSettingsOpen, + openSettingsAnimation: animationController, ), ), ), @@ -40,5 +48,7 @@ void main() { // bottom by utilizing an invisible widget within the Settings Page. expect(tester.getSemantics(find.text('Back')).owner, null); expect(tester.getSemantics(find.text('Front')).label, 'Front'); + + animationController.dispose(); }); }