From fba96809f4102fd2df4a6bf3793eb01ae76cbe05 Mon Sep 17 00:00:00 2001 From: Yuwen Yan Date: Tue, 17 Dec 2019 04:14:24 +0800 Subject: [PATCH] Add enableDrag configuration for showModalBottomSheet (#46685) --- .../lib/src/material/bottom_sheet.dart | 16 ++- .../test/material/bottom_sheet_test.dart | 107 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/material/bottom_sheet.dart b/packages/flutter/lib/src/material/bottom_sheet.dart index 9178dd1ffe5..8cdc948aeb9 100644 --- a/packages/flutter/lib/src/material/bottom_sheet.dart +++ b/packages/flutter/lib/src/material/bottom_sheet.dart @@ -265,7 +265,9 @@ class _ModalBottomSheet extends StatefulWidget { this.shape, this.clipBehavior, this.isScrollControlled = false, + this.enableDrag = true, }) : assert(isScrollControlled != null), + assert(enableDrag != null), super(key: key); final _ModalBottomSheetRoute route; @@ -274,6 +276,7 @@ class _ModalBottomSheet extends StatefulWidget { final double elevation; final ShapeBorder shape; final Clip clipBehavior; + final bool enableDrag; @override _ModalBottomSheetState createState() => _ModalBottomSheetState(); @@ -326,6 +329,7 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> { elevation: widget.elevation, shape: widget.shape, clipBehavior: widget.clipBehavior, + enableDrag: widget.enableDrag, ), ), ), @@ -346,10 +350,12 @@ class _ModalBottomSheetRoute extends PopupRoute { this.clipBehavior, this.modalBarrierColor, this.isDismissible = true, + this.enableDrag = true, @required this.isScrollControlled, RouteSettings settings, }) : assert(isScrollControlled != null), assert(isDismissible != null), + assert(enableDrag != null), super(settings: settings); final WidgetBuilder builder; @@ -361,6 +367,7 @@ class _ModalBottomSheetRoute extends PopupRoute { final Clip clipBehavior; final Color modalBarrierColor; final bool isDismissible; + final bool enableDrag; @override Duration get transitionDuration => _bottomSheetDuration; @@ -399,6 +406,7 @@ class _ModalBottomSheetRoute extends PopupRoute { shape: shape, clipBehavior: clipBehavior, isScrollControlled: isScrollControlled, + enableDrag: enableDrag, ), ); if (theme != null) @@ -437,6 +445,9 @@ class _ModalBottomSheetRoute extends PopupRoute { /// The [isDismissible] parameter specifies whether the bottom sheet will be /// dismissed when user taps on the scrim. /// +/// The [enableDrag] parameter specifies whether the bottom sheet can be +/// dragged up and down and dismissed by swiping downards. +/// /// The optional [backgroundColor], [elevation], [shape], and [clipBehavior] /// parameters can be passed in to customize the appearance and behavior of /// modal bottom sheets. @@ -507,12 +518,14 @@ Future showModalBottomSheet({ bool isScrollControlled = false, bool useRootNavigator = false, bool isDismissible = true, + bool enableDrag = true, }) { assert(context != null); assert(builder != null); assert(isScrollControlled != null); assert(useRootNavigator != null); assert(isDismissible != null); + assert(enableDrag != null); assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMaterialLocalizations(context)); @@ -526,7 +539,8 @@ Future showModalBottomSheet({ shape: shape, clipBehavior: clipBehavior, isDismissible: isDismissible, - modalBarrierColor: barrierColor + modalBarrierColor: barrierColor, + enableDrag: enableDrag, )); } diff --git a/packages/flutter/test/material/bottom_sheet_test.dart b/packages/flutter/test/material/bottom_sheet_test.dart index afb988102f3..43f2e732f8d 100644 --- a/packages/flutter/test/material/bottom_sheet_test.dart +++ b/packages/flutter/test/material/bottom_sheet_test.dart @@ -152,6 +152,113 @@ void main() { expect(find.text('BottomSheet'), findsOneWidget); }); + testWidgets('Swiping down a modal BottomSheet should dismiss it by default', (WidgetTester tester) async { + BuildContext savedContext; + + await tester.pumpWidget(MaterialApp( + home: Builder( + builder: (BuildContext context) { + savedContext = context; + return Container(); + }, + ), + )); + + await tester.pump(); + expect(find.text('BottomSheet'), findsNothing); + + bool showBottomSheetThenCalled = false; + showModalBottomSheet( + context: savedContext, + isDismissible: false, + builder: (BuildContext context) => const Text('BottomSheet'), + ).then((void value) { + showBottomSheetThenCalled = true; + }); + + await tester.pumpAndSettle(); + expect(find.text('BottomSheet'), findsOneWidget); + expect(showBottomSheetThenCalled, isFalse); + + // Swipe the bottom sheet to dismiss it. + await tester.drag(find.text('BottomSheet'), const Offset(0.0, 150.0)); + await tester.pumpAndSettle(); // Bottom sheet dismiss animation. + expect(showBottomSheetThenCalled, isTrue); + expect(find.text('BottomSheet'), findsNothing); + }); + + testWidgets('Swiping down a modal BottomSheet should not dismiss it when enableDrag is false', (WidgetTester tester) async { + BuildContext savedContext; + + await tester.pumpWidget(MaterialApp( + home: Builder( + builder: (BuildContext context) { + savedContext = context; + return Container(); + }, + ), + )); + + await tester.pump(); + expect(find.text('BottomSheet'), findsNothing); + + bool showBottomSheetThenCalled = false; + showModalBottomSheet( + context: savedContext, + isDismissible: false, + enableDrag: false, + builder: (BuildContext context) => const Text('BottomSheet'), + ).then((void value) { + showBottomSheetThenCalled = true; + }); + + await tester.pumpAndSettle(); + expect(find.text('BottomSheet'), findsOneWidget); + expect(showBottomSheetThenCalled, isFalse); + + // Swipe the bottom sheet, attempting to dismiss it. + await tester.drag(find.text('BottomSheet'), const Offset(0.0, 150.0)); + await tester.pumpAndSettle(); // Bottom sheet should not dismiss. + expect(showBottomSheetThenCalled, isFalse); + expect(find.text('BottomSheet'), findsOneWidget); + }); + + testWidgets('Swiping down a modal BottomSheet should dismiss it when enableDrag is true', (WidgetTester tester) async { + BuildContext savedContext; + + await tester.pumpWidget(MaterialApp( + home: Builder( + builder: (BuildContext context) { + savedContext = context; + return Container(); + }, + ), + )); + + await tester.pump(); + expect(find.text('BottomSheet'), findsNothing); + + bool showBottomSheetThenCalled = false; + showModalBottomSheet( + context: savedContext, + isDismissible: false, + enableDrag: true, + builder: (BuildContext context) => const Text('BottomSheet'), + ).then((void value) { + showBottomSheetThenCalled = true; + }); + + await tester.pumpAndSettle(); + expect(find.text('BottomSheet'), findsOneWidget); + expect(showBottomSheetThenCalled, isFalse); + + // Swipe the bottom sheet to dismiss it. + await tester.drag(find.text('BottomSheet'), const Offset(0.0, 150.0)); + await tester.pumpAndSettle(); // Bottom sheet dismiss animation. + expect(showBottomSheetThenCalled, isTrue); + expect(find.text('BottomSheet'), findsNothing); + }); + testWidgets('Verify that a downwards fling dismisses a persistent BottomSheet', (WidgetTester tester) async { final GlobalKey scaffoldKey = GlobalKey(); bool showBottomSheetThenCalled = false;