Unverified Commit 0135a331 authored by auto-submit[bot]'s avatar auto-submit[bot] Committed by GitHub

Reverts "Introduce `AnimationStyle`" (#138628)

Reverts flutter/flutter#137945
Initiated by: HansMuller
This change reverts the following previous change:
Original Description:
This PR introduces `AnimationStyle`, it is used to override default animation curves and durations in several widgets.

fixes  [Add the ability to customize MaterialApp theme animation duration](https://github.com/flutter/flutter/issues/78372)
fixes [Allow customization of showMenu transition animation curves and duration](https://github.com/flutter/flutter/issues/135638)

Here is an example where popup menu curve and transition duration is overriden:

```dart
          popUpAnimationStyle: AnimationStyle(
            curve: Easing.emphasizedAccelerate,
            duration: Durations.medium4,
          ),
```

Set `AnimationStyle.noAnimation` to disable animation.
```dart
    return MaterialApp(
      themeAnimationStyle: AnimationStyle.noAnimation,
```
parent e9de4484
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
/// Flutter code sample for [MaterialApp].
void main() {
runApp(const MaterialAppExample());
}
enum AnimationStyles { defaultStyle, custom, none }
const List<(AnimationStyles, String)> animationStyleSegments = <(AnimationStyles, String)>[
(AnimationStyles.defaultStyle, 'Default'),
(AnimationStyles.custom, 'Custom'),
(AnimationStyles.none, 'None'),
];
class MaterialAppExample extends StatefulWidget {
const MaterialAppExample({super.key});
@override
State<MaterialAppExample> createState() => _MaterialAppExampleState();
}
class _MaterialAppExampleState extends State<MaterialAppExample> {
Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{AnimationStyles.defaultStyle};
AnimationStyle? _animationStyle;
bool isDarkTheme = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
themeAnimationStyle: _animationStyle,
themeMode: isDarkTheme ? ThemeMode.dark : ThemeMode.light,
theme: ThemeData(colorSchemeSeed: Colors.green),
darkTheme: ThemeData(
colorSchemeSeed: Colors.green,
brightness: Brightness.dark,
),
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SegmentedButton<AnimationStyles>(
selected: _animationStyleSelection,
onSelectionChanged: (Set<AnimationStyles> styles) {
setState(() {
_animationStyleSelection = styles;
switch (styles.first) {
case AnimationStyles.defaultStyle:
_animationStyle = null;
case AnimationStyles.custom:
_animationStyle = AnimationStyle(
curve: Easing.emphasizedAccelerate,
duration: const Duration(seconds: 1),
);
case AnimationStyles.none:
_animationStyle = AnimationStyle.noAnimation;
}
});
},
segments: animationStyleSegments
.map<ButtonSegment<AnimationStyles>>(((AnimationStyles, String) shirt) {
return ButtonSegment<AnimationStyles>(value: shirt.$1, label: Text(shirt.$2));
})
.toList(),
),
const SizedBox(height: 10),
OutlinedButton.icon(
onPressed: () {
setState(() {
isDarkTheme = !isDarkTheme;
});
},
icon: Icon(isDarkTheme ? Icons.wb_sunny : Icons.nightlight_round),
label: const Text('Switch Theme Mode'),
),
],
),
),
),
);
}
}
......@@ -30,7 +30,7 @@ class PopupMenuExample extends StatefulWidget {
}
class _PopupMenuExampleState extends State<PopupMenuExample> {
SampleItem? selectedItem;
SampleItem? selectedMenu;
@override
Widget build(BuildContext context) {
......@@ -38,10 +38,11 @@ class _PopupMenuExampleState extends State<PopupMenuExample> {
appBar: AppBar(title: const Text('PopupMenuButton')),
body: Center(
child: PopupMenuButton<SampleItem>(
initialValue: selectedItem,
initialValue: selectedMenu,
// Callback that sets the selected popup menu item.
onSelected: (SampleItem item) {
setState(() {
selectedItem = item;
selectedMenu = item;
});
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<SampleItem>>[
......
......@@ -31,7 +31,7 @@ class PopupMenuExample extends StatefulWidget {
}
class _PopupMenuExampleState extends State<PopupMenuExample> {
SampleItem? selectedItem;
SampleItem? selectedMenu;
@override
Widget build(BuildContext context) {
......@@ -39,10 +39,11 @@ class _PopupMenuExampleState extends State<PopupMenuExample> {
appBar: AppBar(title: const Text('PopupMenuButton')),
body: Center(
child: PopupMenuButton<SampleItem>(
initialValue: selectedItem,
initialValue: selectedMenu,
// Callback that sets the selected popup menu item.
onSelected: (SampleItem item) {
setState(() {
selectedItem = item;
selectedMenu = item;
});
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<SampleItem>>[
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
/// Flutter code sample for [PopupMenuButton].
void main() => runApp(const PopupMenuApp());
class PopupMenuApp extends StatelessWidget {
const PopupMenuApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: PopupMenuExample(),
);
}
}
enum AnimationStyles { defaultStyle, custom, none }
const List<(AnimationStyles, String)> animationStyleSegments = <(AnimationStyles, String)>[
(AnimationStyles.defaultStyle, 'Default'),
(AnimationStyles.custom, 'Custom'),
(AnimationStyles.none, 'None'),
];
enum Menu { preview, share, getLink, remove, download }
class PopupMenuExample extends StatefulWidget {
const PopupMenuExample({super.key});
@override
State<PopupMenuExample> createState() => _PopupMenuExampleState();
}
class _PopupMenuExampleState extends State<PopupMenuExample> {
Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{AnimationStyles.defaultStyle};
AnimationStyle? _animationStyle;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.only(top: 50),
child: Align(
alignment: Alignment.topCenter,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SegmentedButton<AnimationStyles>(
selected: _animationStyleSelection,
onSelectionChanged: (Set<AnimationStyles> styles) {
setState(() {
_animationStyleSelection = styles;
switch (styles.first) {
case AnimationStyles.defaultStyle:
_animationStyle = null;
case AnimationStyles.custom:
_animationStyle = AnimationStyle(
curve: Easing.emphasizedDecelerate,
duration: const Duration(seconds: 3),
);
case AnimationStyles.none:
_animationStyle = AnimationStyle.noAnimation;
}
});
},
segments: animationStyleSegments
.map<ButtonSegment<AnimationStyles>>(((AnimationStyles, String) shirt) {
return ButtonSegment<AnimationStyles>(value: shirt.$1, label: Text(shirt.$2));
})
.toList(),
),
const SizedBox(height: 10),
PopupMenuButton<Menu>(
popUpAnimationStyle: _animationStyle,
icon: const Icon(Icons.more_vert),
onSelected: (Menu item) { },
itemBuilder: (BuildContext context) => <PopupMenuEntry<Menu>>[
const PopupMenuItem<Menu>(
value: Menu.preview,
child: ListTile(
leading: Icon(Icons.visibility_outlined),
title: Text('Preview'),
),
),
const PopupMenuItem<Menu>(
value: Menu.share,
child: ListTile(
leading: Icon(Icons.share_outlined),
title: Text('Share'),
),
),
const PopupMenuItem<Menu>(
value: Menu.getLink,
child: ListTile(
leading: Icon(Icons.link_outlined),
title: Text('Get link'),
),
),
const PopupMenuDivider(),
const PopupMenuItem<Menu>(
value: Menu.remove,
child: ListTile(
leading: Icon(Icons.delete_outline),
title: Text('Remove'),
),
),
const PopupMenuItem<Menu>(
value: Menu.download,
child: ListTile(
leading: Icon(Icons.download_outlined),
title: Text('Download'),
),
),
],
),
],
),
),
),
),
);
}
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_api_samples/material/app/app.0.dart'
as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Theme Animation can be customized using AnimationStyle', (WidgetTester tester) async {
await tester.pumpWidget(
const example.MaterialAppExample(),
);
Material getScaffoldMaterial() {
return tester.widget<Material>(find.descendant(
of: find.byType(Scaffold),
matching: find.byType(Material).first,
));
}
final ThemeData lightTheme = ThemeData(colorSchemeSeed: Colors.green);
final ThemeData darkTheme = ThemeData(
colorSchemeSeed: Colors.green,
brightness: Brightness.dark,
);
// Test the default animation.
expect(getScaffoldMaterial().color, lightTheme.colorScheme.background);
await tester.tap(find.text( 'Switch Theme Mode'));
await tester.pump();
// Advance the animation by half of the default duration.
await tester.pump(const Duration(milliseconds: 100));
// The Scaffold background color is updated.
expect(
getScaffoldMaterial().color,
Color.lerp(lightTheme.colorScheme.background, darkTheme.colorScheme.background, 0.5),
);
await tester.pumpAndSettle();
// The Scaffold background color is now fully dark.
expect(getScaffoldMaterial().color, darkTheme.colorScheme.background);
// Test the custom animation curve and duration.
await tester.tap(find.text('Custom'));
await tester.pumpAndSettle();
await tester.tap(find.text('Switch Theme Mode'));
await tester.pump();
// Advance the animation by half of the custom duration.
await tester.pump(const Duration(milliseconds: 500));
// The Scaffold background color is updated.
expect(getScaffoldMaterial().color, const Color(0xff3c3e3b));
await tester.pumpAndSettle();
// The Scaffold background color is now fully light.
expect(getScaffoldMaterial().color, lightTheme.colorScheme.background);
// Test the no animation style.
await tester.tap(find.text('None'));
await tester.pumpAndSettle();
await tester.tap(find.text('Switch Theme Mode'));
// Advance the animation by only one frame.
await tester.pump();
// The Scaffold background color is updated immediately.
expect(getScaffoldMaterial().color, darkTheme.colorScheme.background);
});
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_api_samples/material/popup_menu/popup_menu.2.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Popup animation can be customized using AnimationStyle', (WidgetTester tester) async {
await tester.pumpWidget(
const example.PopupMenuApp(),
);
// Test the default popup animation.
await tester.tap(find.byIcon(Icons.more_vert));
await tester.pump();
// Advance the animation by half of the default duration.
await tester.pump(const Duration(milliseconds: 100));
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(224.0, 130.0)));
// Let the animation finish.
await tester.pumpAndSettle();
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(224.0, 312.0)));
// Tap outside the popup menu to close it.
await tester.tapAt(const Offset(1, 1));
await tester.pumpAndSettle();
// Test the custom animation curve and duration.
await tester.tap(find.text('Custom'));
await tester.pumpAndSettle();
await tester.tap(find.byIcon(Icons.more_vert));
await tester.pump();
// Advance the animation by one third of the custom duration.
await tester.pump(const Duration(milliseconds: 1000));
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(224.0, 312.0)));
// Let the animation finish.
await tester.pumpAndSettle();
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(224.0, 312.0)));
// Tap outside the popup menu to close it.
await tester.tapAt(const Offset(1, 1));
await tester.pumpAndSettle();
// Test the no animation style.
await tester.tap(find.text('None'));
await tester.pumpAndSettle();
await tester.tap(find.byIcon(Icons.more_vert));
// Advance the animation by only one frame.
await tester.pump();
// The popup menu is shown immediately.
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(224.0, 312.0)));
});
}
......@@ -165,7 +165,6 @@ export 'package:flutter/scheduler.dart' show TickerCanceled;
export 'src/animation/animation.dart';
export 'src/animation/animation_controller.dart';
export 'src/animation/animation_style.dart';
export 'src/animation/animations.dart';
export 'src/animation/curves.dart';
export 'src/animation/listener_helpers.dart';
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'curves.dart';
/// Used to override the default parameters of an animation.
///
/// Currently, this class is used by the following widgets:
/// - [ExpansionTile]
/// - [MaterialApp]
/// - [PopupMenuButton]
///
/// If [duration] and [reverseDuration] are set to [Duration.zero], the
/// corresponding animation will be disabled.
///
/// All of the parameters are optional. If no parameters are specified,
/// the default animation will be used.
class AnimationStyle {
/// Creates an instance of Animation Style class.
AnimationStyle({
this.curve,
this.duration,
this.reverseCurve,
this.reverseDuration,
});
/// Creates an instance of Animation Style class with no animation.
static AnimationStyle noAnimation = AnimationStyle(
duration: Duration.zero,
reverseDuration: Duration.zero,
);
/// When specified, the animation will use this curve.
final Curve? curve;
/// When specified, the animation will use this duration.
final Duration? duration;
/// When specified, the reverse animation will use this curve.
final Curve? reverseCurve;
/// When specified, the reverse animation will use this duration.
final Duration? reverseDuration;
}
......@@ -184,7 +184,6 @@ enum ThemeMode {
/// ),
/// )
/// ```
///
/// See also:
///
/// * [Scaffold], which provides standard app elements like an [AppBar] and a [Drawer].
......@@ -247,7 +246,6 @@ class MaterialApp extends StatefulWidget {
'This feature was deprecated after v3.7.0-29.0.pre.'
)
this.useInheritedMediaQuery = false,
this.themeAnimationStyle,
}) : routeInformationProvider = null,
routeInformationParser = null,
routerDelegate = null,
......@@ -298,7 +296,6 @@ class MaterialApp extends StatefulWidget {
'This feature was deprecated after v3.7.0-29.0.pre.'
)
this.useInheritedMediaQuery = false,
this.themeAnimationStyle,
}) : assert(routerDelegate != null || routerConfig != null),
navigatorObservers = null,
navigatorKey = null,
......@@ -756,28 +753,6 @@ class MaterialApp extends StatefulWidget {
)
final bool useInheritedMediaQuery;
/// Used to override the theme animation curve and duration.
///
/// If [AnimationStyle.duration] is provided, it will be used to override
/// the theme animation duration in the underlying [AnimatedTheme] widget.
/// If it is null, then [themeAnimationDuration] will be used. Otherwise,
/// defaults to 200ms.
///
/// If [AnimationStyle.curve] is provided, it will be used to override
/// the theme animation curve in the underlying [AnimatedTheme] widget.
/// If it is null, then [themeAnimationCurve] will be used. Otherwise,
/// defaults to [Curves.linear].
///
/// To disable the theme animation, use [AnimationStyle.noAnimation].
///
/// {@tool dartpad}
/// This sample showcases how to override the theme animation curve and
/// duration in the [MaterialApp] widget using [AnimationStyle].
///
/// ** See code in examples/api/lib/material/app/app.0.dart **
/// {@end-tool}
final AnimationStyle? themeAnimationStyle;
@override
State<MaterialApp> createState() => _MaterialAppState();
......@@ -955,34 +930,6 @@ class _MaterialAppState extends State<MaterialApp> {
final Color effectiveSelectionColor = theme.textSelectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
final Color effectiveCursorColor = theme.textSelectionTheme.cursorColor ?? theme.colorScheme.primary;
Widget childWidget = widget.builder != null
? Builder(
builder: (BuildContext context) {
// Why are we surrounding a builder with a builder?
//
// The widget.builder may contain code that invokes
// Theme.of(), which should return the theme we selected
// above in AnimatedTheme. However, if we invoke
// widget.builder() directly as the child of AnimatedTheme
// then there is no Context separating them, and the
// widget.builder() will not find the theme. Therefore, we
// surround widget.builder with yet another builder so that
// a context separates them and Theme.of() correctly
// resolves to the theme we passed to AnimatedTheme.
return widget.builder!(context, child);
},
)
: child ?? const SizedBox.shrink();
if (widget.themeAnimationStyle != AnimationStyle.noAnimation) {
childWidget = AnimatedTheme(
data: theme,
duration: widget.themeAnimationStyle?.duration ?? widget.themeAnimationDuration,
curve: widget.themeAnimationStyle?.curve ?? widget.themeAnimationCurve,
child: childWidget,
);
}
return ScaffoldMessenger(
key: widget.scaffoldMessengerKey,
child: DefaultSelectionStyle(
......@@ -990,9 +937,26 @@ class _MaterialAppState extends State<MaterialApp> {
cursorColor: effectiveCursorColor,
child: AnimatedTheme(
data: theme,
duration: widget.themeAnimationStyle?.duration ?? widget.themeAnimationDuration,
curve: widget.themeAnimationStyle?.curve ?? widget.themeAnimationCurve,
child: childWidget,
duration: widget.themeAnimationDuration,
curve: widget.themeAnimationCurve,
child: widget.builder != null
? Builder(
builder: (BuildContext context) {
// Why are we surrounding a builder with a builder?
//
// The widget.builder may contain code that invokes
// Theme.of(), which should return the theme we selected
// above in AnimatedTheme. However, if we invoke
// widget.builder() directly as the child of AnimatedTheme
// then there is no Context separating them, and the
// widget.builder() will not find the theme. Therefore, we
// surround widget.builder with yet another builder so that
// a context separates them and Theme.of() correctly
// resolves to the theme we passed to AnimatedTheme.
return widget.builder!(context, child);
},
)
: child ?? const SizedBox.shrink(),
),
),
);
......
......@@ -816,7 +816,6 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
this.constraints,
required this.clipBehavior,
super.settings,
this.popUpAnimationStyle,
}) : itemSizes = List<Size?>.filled(items.length, null),
// Menus always cycle focus through their items irrespective of the
// focus traversal edge behavior set in the Navigator.
......@@ -835,22 +834,18 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
final CapturedThemes capturedThemes;
final BoxConstraints? constraints;
final Clip clipBehavior;
final AnimationStyle? popUpAnimationStyle;
@override
Animation<double> createAnimation() {
if (popUpAnimationStyle != AnimationStyle.noAnimation) {
return CurvedAnimation(
parent: super.createAnimation(),
curve: popUpAnimationStyle?.curve ?? Curves.linear,
reverseCurve: popUpAnimationStyle?.reverseCurve ?? const Interval(0.0, _kMenuCloseIntervalEnd),
);
}
return super.createAnimation();
return CurvedAnimation(
parent: super.createAnimation(),
curve: Curves.linear,
reverseCurve: const Interval(0.0, _kMenuCloseIntervalEnd),
);
}
@override
Duration get transitionDuration => popUpAnimationStyle?.duration ?? _kMenuDuration;
Duration get transitionDuration => _kMenuDuration;
@override
bool get barrierDismissible => true;
......@@ -982,7 +977,6 @@ Future<T?> showMenu<T>({
BoxConstraints? constraints,
Clip clipBehavior = Clip.none,
RouteSettings? routeSettings,
AnimationStyle? popUpAnimationStyle,
}) {
assert(items.isNotEmpty);
assert(debugCheckHasMaterialLocalizations(context));
......@@ -1014,7 +1008,6 @@ Future<T?> showMenu<T>({
constraints: constraints,
clipBehavior: clipBehavior,
settings: routeSettings,
popUpAnimationStyle: popUpAnimationStyle,
));
}
......@@ -1102,13 +1095,6 @@ typedef PopupMenuItemBuilder<T> = List<PopupMenuEntry<T>> Function(BuildContext
/// ** See code in examples/api/lib/material/popup_menu/popup_menu.1.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// This sample showcases how to override the [PopupMenuButton] animation
/// curves and duration using [AnimationStyle].
///
/// ** See code in examples/api/lib/material/popup_menu/popup_menu.2.dart **
/// {@end-tool}
///
/// See also:
///
/// * [PopupMenuItem], a popup menu entry for a single value.
......@@ -1143,7 +1129,6 @@ class PopupMenuButton<T> extends StatefulWidget {
this.position,
this.clipBehavior = Clip.none,
this.useRootNavigator = false,
this.popUpAnimationStyle,
}) : assert(
!(child != null && icon != null),
'You can only pass [child] or [icon], not both.',
......@@ -1314,24 +1299,6 @@ class PopupMenuButton<T> extends StatefulWidget {
/// Defaults to false.
final bool useRootNavigator;
/// Used to override the default animation curves and durations of the popup
/// menu's open and close transitions.
///
/// If [AnimationStyle.curve] is provided, it will be used to override
/// the default popup animation curve. Otherwise, defaults to [Curves.linear].
///
/// If [AnimationStyle.reverseCurve] is provided, it will be used to
/// override the default popup animation reverse curve. Otherwise, defaults to
/// `Interval(0.0, 2.0 / 3.0)`.
///
/// If [AnimationStyle.duration] is provided, it will be used to override
/// the default popup animation duration. Otherwise, defaults to 300ms.
///
/// To disable the theme animation, use [AnimationStyle.noAnimation].
///
/// If this is null, then the default animation will be used.
final AnimationStyle? popUpAnimationStyle;
@override
PopupMenuButtonState<T> createState() => PopupMenuButtonState<T>();
}
......@@ -1389,7 +1356,6 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
constraints: widget.constraints,
clipBehavior: widget.clipBehavior,
useRootNavigator: widget.useRootNavigator,
popUpAnimationStyle: widget.popUpAnimationStyle,
)
.then<void>((T? newValue) {
if (!mounted) {
......
......@@ -1525,71 +1525,6 @@ void main() {
defaultBehavior.buildScrollbar(capturedContext, child, details);
}
}, variant: TargetPlatformVariant.all());
testWidgetsWithLeakTracking('Override theme animation using AnimationStyle', (WidgetTester tester) async {
final ThemeData lightTheme = ThemeData.light();
final ThemeData darkTheme = ThemeData.dark();
Widget buildWidget({ ThemeMode themeMode = ThemeMode.light, AnimationStyle? animationStyle }) {
return MaterialApp(
theme: lightTheme,
darkTheme: darkTheme,
themeMode: themeMode,
themeAnimationStyle: animationStyle,
home: const Scaffold(body: Text('body')),
);
}
// Test the initial Scaffold background color.
await tester.pumpWidget(buildWidget());
expect(tester.widget<Material>(find.byType(Material)).color, const Color(0xfffffbfe));
// Test the Scaffold background color animation from light to dark theme.
await tester.pumpWidget(buildWidget(themeMode: ThemeMode.dark));
await tester.pump();
await tester.pump(const Duration(milliseconds: 50)); // Advance animation by 50 milliseconds.
// Scaffold background color is slightly updated.
expect(tester.widget<Material>(find.byType(Material)).color, const Color(0xffc6c3c6));
// Let the animation finish.
await tester.pumpAndSettle();
// Scaffold background color is fully updated to dark theme.
expect(tester.widget<Material>(find.byType(Material)).color, const Color(0xff1c1b1f));
// Reset to light theme to compare the Scaffold background color animation
// with the default animation curve.
await tester.pumpWidget(buildWidget());
await tester.pumpAndSettle();
// Switch to dark theme with overriden animation curve.
await tester.pumpWidget(buildWidget(
themeMode: ThemeMode.dark,
animationStyle: AnimationStyle(curve: Curves.easeIn,
)));
await tester.pump();
await tester.pump(const Duration(milliseconds: 50));
// Scaffold background color is slightly updated but with a different
// color than the default animation curve.
expect(tester.widget<Material>(find.byType(Material)).color, const Color(0xffe9e5e9));
// Let the animation finish.
await tester.pumpAndSettle();
// Scaffold background color is fully updated to dark theme.
expect(tester.widget<Material>(find.byType(Material)).color, const Color(0xff1c1b1f));
// Switch from dark to light theme with overriden animation duration.
await tester.pumpWidget(buildWidget(animationStyle: AnimationStyle.noAnimation));
await tester.pump();
await tester.pump(const Duration(milliseconds: 1));
expect(tester.widget<Material>(find.byType(Material)).color, isNot(const Color(0xff1c1b1f)));
expect(tester.widget<Material>(find.byType(Material)).color, const Color(0xfffffbfe));
});
}
class MockScrollBehavior extends ScrollBehavior {
......
......@@ -3872,90 +3872,6 @@ void main() {
expect(rootObserver.menuCount, 0);
expect(nestedObserver.menuCount, 1);
});
testWidgetsWithLeakTracking('Override Popup Menu animation using AnimationStyle', (WidgetTester tester) async {
final Key targetKey = UniqueKey();
Widget buildPopupMenu({ AnimationStyle? animationStyle }) {
return MaterialApp(
home: Material(
child: Center(
child: PopupMenuButton<int>(
key: targetKey,
popUpAnimationStyle: animationStyle,
itemBuilder: (BuildContext context) {
return <PopupMenuItem<int>>[
const PopupMenuItem<int>(
value: 1,
child: Text('One'),
),
const PopupMenuItem<int>(
value: 2,
child: Text('Two'),
),
const PopupMenuItem<int>(
value: 3,
child: Text('Three'),
),
];
},
),
),
),
);
}
// Test default animation.
await tester.pumpWidget(buildPopupMenu());
await tester.tap(find.byKey(targetKey));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100)); // Advance the animation by 1/3 of its duration.
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(112.0, 80.0)));
await tester.pump(const Duration(milliseconds: 100)); // Advance the animation by 2/3 of its duration.
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(112.0, 160.0)));
await tester.pumpAndSettle(); // Advance the animation to the end.
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(112.0, 160.0)));
// Tap outside to dismiss the menu.
await tester.tapAt(const Offset(20.0, 20.0));
await tester.pumpAndSettle();
// Override the animation duration.
await tester.pumpWidget(buildPopupMenu(animationStyle: AnimationStyle(duration: Duration.zero)));
await tester.tap(find.byKey(targetKey));
await tester.pump();
await tester.pump(const Duration(milliseconds: 1)); // Advance the animation by 1 millisecond.
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(112.0, 160.0)));
// Tap outside to dismiss the menu.
await tester.tapAt(const Offset(20.0, 20.0));
await tester.pumpAndSettle();
// Override the animation curve.
await tester.pumpWidget(buildPopupMenu(animationStyle: AnimationStyle(curve: Easing.emphasizedAccelerate)));
await tester.tap(find.byKey(targetKey));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100)); // Advance the animation by 1/3 of its duration.
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(32.4, 15.4)));
await tester.pump(const Duration(milliseconds: 100)); // Advance the animation by 2/3 of its duration.
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(112.0, 72.2)));
await tester.pumpAndSettle(); // Advance the animation to the end.
expect(tester.getSize(find.byType(Material).last), within(distance: 0.1, from: const Size(112.0, 160.0)));
});
}
class TestApp extends StatelessWidget {
......
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