Unverified Commit 46b0797b authored by Zain Ur Rehman's avatar Zain Ur Rehman Committed by GitHub

Expose AnimationController property to showBottomSheet/showModalBottomSheet (#72541)

* Expose AnimationController property to showBottomSheet/showModalBottomSheet (#72541)
ong <shihaohong@google.com>
Co-authored-by: 's avatarKate Lovett <katelovett@google.com>
Co-authored-by: 's avatarShi-Hao Hong <shihaohong@google.com>
parent 32db0fe7
...@@ -431,6 +431,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -431,6 +431,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
this.enableDrag = true, this.enableDrag = true,
required this.isScrollControlled, required this.isScrollControlled,
RouteSettings? settings, RouteSettings? settings,
this.transitionAnimationController,
}) : assert(isScrollControlled != null), }) : assert(isScrollControlled != null),
assert(isDismissible != null), assert(isDismissible != null),
assert(enableDrag != null), assert(enableDrag != null),
...@@ -446,6 +447,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -446,6 +447,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
final Color? modalBarrierColor; final Color? modalBarrierColor;
final bool isDismissible; final bool isDismissible;
final bool enableDrag; final bool enableDrag;
final AnimationController? transitionAnimationController;
@override @override
Duration get transitionDuration => _bottomSheetEnterDuration; Duration get transitionDuration => _bottomSheetEnterDuration;
...@@ -467,7 +469,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -467,7 +469,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
@override @override
AnimationController createAnimationController() { AnimationController createAnimationController() {
assert(_animationController == null); assert(_animationController == null);
_animationController = BottomSheet.createAnimationController(navigator!.overlay!); _animationController = transitionAnimationController ?? BottomSheet.createAnimationController(navigator!.overlay!);
return _animationController!; return _animationController!;
} }
...@@ -588,10 +590,13 @@ class _BottomSheetSuspendedCurve extends ParametricCurve<double> { ...@@ -588,10 +590,13 @@ class _BottomSheetSuspendedCurve extends ParametricCurve<double> {
/// The [enableDrag] parameter specifies whether the bottom sheet can be /// The [enableDrag] parameter specifies whether the bottom sheet can be
/// dragged up and down and dismissed by swiping downwards. /// dragged up and down and dismissed by swiping downwards.
/// ///
/// The optional [backgroundColor], [elevation], [shape], and [clipBehavior] /// The optional [backgroundColor], [elevation], [shape], [clipBehavior] and [transitionAnimationController]
/// parameters can be passed in to customize the appearance and behavior of /// parameters can be passed in to customize the appearance and behavior of
/// modal bottom sheets. /// modal bottom sheets.
/// ///
/// The [transitionAnimationController] controls the bottom sheet's entrance and
/// exit animations if provided.
///
/// The optional `routeSettings` parameter sets the [RouteSettings] of the modal bottom sheet /// The optional `routeSettings` parameter sets the [RouteSettings] of the modal bottom sheet
/// sheet. This is particularly useful in the case that a user wants to observe /// sheet. This is particularly useful in the case that a user wants to observe
/// [PopupRoute]s within a [NavigatorObserver]. /// [PopupRoute]s within a [NavigatorObserver].
...@@ -662,6 +667,7 @@ Future<T?> showModalBottomSheet<T>({ ...@@ -662,6 +667,7 @@ Future<T?> showModalBottomSheet<T>({
bool isDismissible = true, bool isDismissible = true,
bool enableDrag = true, bool enableDrag = true,
RouteSettings? routeSettings, RouteSettings? routeSettings,
AnimationController? transitionAnimationController,
}) { }) {
assert(context != null); assert(context != null);
assert(builder != null); assert(builder != null);
...@@ -686,6 +692,7 @@ Future<T?> showModalBottomSheet<T>({ ...@@ -686,6 +692,7 @@ Future<T?> showModalBottomSheet<T>({
modalBarrierColor: barrierColor, modalBarrierColor: barrierColor,
enableDrag: enableDrag, enableDrag: enableDrag,
settings: routeSettings, settings: routeSettings,
transitionAnimationController: transitionAnimationController,
)); ));
} }
...@@ -695,7 +702,7 @@ Future<T?> showModalBottomSheet<T>({ ...@@ -695,7 +702,7 @@ Future<T?> showModalBottomSheet<T>({
/// Returns a controller that can be used to close and otherwise manipulate the /// Returns a controller that can be used to close and otherwise manipulate the
/// bottom sheet. /// bottom sheet.
/// ///
/// The optional [backgroundColor], [elevation], [shape], and [clipBehavior] /// The optional [backgroundColor], [elevation], [shape], [clipBehavior] and [transitionAnimationController]
/// parameters can be passed in to customize the appearance and behavior of /// parameters can be passed in to customize the appearance and behavior of
/// persistent bottom sheets. /// persistent bottom sheets.
/// ///
...@@ -735,6 +742,7 @@ PersistentBottomSheetController<T> showBottomSheet<T>({ ...@@ -735,6 +742,7 @@ PersistentBottomSheetController<T> showBottomSheet<T>({
double? elevation, double? elevation,
ShapeBorder? shape, ShapeBorder? shape,
Clip? clipBehavior, Clip? clipBehavior,
AnimationController? transitionAnimationController,
}) { }) {
assert(context != null); assert(context != null);
assert(builder != null); assert(builder != null);
...@@ -746,5 +754,6 @@ PersistentBottomSheetController<T> showBottomSheet<T>({ ...@@ -746,5 +754,6 @@ PersistentBottomSheetController<T> showBottomSheet<T>({
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior, clipBehavior: clipBehavior,
transitionAnimationController: transitionAnimationController,
); );
} }
...@@ -2634,6 +2634,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto ...@@ -2634,6 +2634,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
double? elevation, double? elevation,
ShapeBorder? shape, ShapeBorder? shape,
Clip? clipBehavior, Clip? clipBehavior,
AnimationController? transitionAnimationController,
}) { }) {
assert(() { assert(() {
if (widget.bottomSheet != null) { if (widget.bottomSheet != null) {
...@@ -2648,7 +2649,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto ...@@ -2648,7 +2649,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMediaQuery(context));
_closeCurrentBottomSheet(); _closeCurrentBottomSheet();
final AnimationController controller = BottomSheet.createAnimationController(this)..forward(); final AnimationController controller = (transitionAnimationController ?? BottomSheet.createAnimationController(this))..forward();
setState(() { setState(() {
_currentBottomSheet = _buildBottomSheet<T>( _currentBottomSheet = _buildBottomSheet<T>(
builder, builder,
......
...@@ -727,6 +727,127 @@ void main() { ...@@ -727,6 +727,127 @@ void main() {
expect(retrievedRouteSettings, routeSettings); expect(retrievedRouteSettings, routeSettings);
}); });
testWidgets('Verify showModalBottomSheet use AnimationController if provided.', (WidgetTester tester) async {
const Key tapTarget = Key('tap-target');
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
showModalBottomSheet<void>(
context: context,
// The default duration and reverseDuration is 1 second
transitionAnimationController: AnimationController(
vsync: const TestVSync(),
duration: const Duration(seconds: 2),
reverseDuration: const Duration(seconds: 2),
),
builder: (BuildContext context) {
return Container(
child: const Text('BottomSheet'),
);
},
);
},
behavior: HitTestBehavior.opaque,
child: Container(
height: 100.0,
width: 100.0,
key: tapTarget,
),
);
},
),
),
));
expect(find.text('BottomSheet'), findsNothing);
await tester.tap(find.byKey(tapTarget)); // Opening animation will start after tapping
await tester.pump();
expect(find.text('BottomSheet'), findsOneWidget);
await tester.pump(const Duration(milliseconds: 2000));
expect(find.text('BottomSheet'), findsOneWidget);
// Tapping above the bottom sheet to dismiss it.
await tester.tapAt(const Offset(20.0, 20.0)); // Closing animation will start after tapping
await tester.pump();
expect(find.text('BottomSheet'), findsOneWidget);
await tester.pump(const Duration(milliseconds: 2000));
// The bottom sheet should still be present at the very end of the animation.
expect(find.text('BottomSheet'), findsOneWidget);
await tester.pump(const Duration(milliseconds: 1));
// The bottom sheet should not be showing any longer.
expect(find.text('BottomSheet'), findsNothing);
});
testWidgets('Verify persistence BottomSheet use AnimationController if provided.', (WidgetTester tester) async {
const Key tapTarget = Key('tap-target');
const Key tapTargetToClose = Key('tap-target-to-close');
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
showBottomSheet<void>(
context: context,
// The default duration and reverseDuration is 1 second
transitionAnimationController: AnimationController(
vsync: const TestVSync(),
duration: const Duration(seconds: 2),
reverseDuration: const Duration(seconds: 2),
),
builder: (BuildContext context) {
return Container(
child: MaterialButton(
child: const Text('BottomSheet'),
onPressed: () => Navigator.pop(context),
key: tapTargetToClose,
),
);
},
);
},
behavior: HitTestBehavior.opaque,
child: Container(
height: 100.0,
width: 100.0,
key: tapTarget,
),
);
},
),
),
));
expect(find.text('BottomSheet'), findsNothing);
await tester.tap(find.byKey(tapTarget)); // Opening animation will start after tapping
await tester.pump();
expect(find.text('BottomSheet'), findsOneWidget);
await tester.pump(const Duration(milliseconds: 2000));
expect(find.text('BottomSheet'), findsOneWidget);
// Tapping button on the bottom sheet to dismiss it.
await tester.tap(find.byKey(tapTargetToClose)); // Closing animation will start after tapping
await tester.pump();
expect(find.text('BottomSheet'), findsOneWidget);
await tester.pump(const Duration(milliseconds: 2000));
// The bottom sheet should still be present at the very end of the animation.
expect(find.text('BottomSheet'), findsOneWidget);
await tester.pump(const Duration(milliseconds: 1));
// The bottom sheet should not be showing any longer.
expect(find.text('BottomSheet'), findsNothing);
});
testWidgets('showModalBottomSheet should move along on-screen keyboard', testWidgets('showModalBottomSheet should move along on-screen keyboard',
(WidgetTester tester) async { (WidgetTester tester) async {
late BuildContext savedContext; late BuildContext savedContext;
......
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