From 74e564da2d3e7a710f1f16a01ccdc7631b693a0c Mon Sep 17 00:00:00 2001 From: Per Classon Date: Thu, 20 Feb 2020 03:51:04 +0100 Subject: [PATCH] Add parameter to Scaffold so its possible to disable open Drawer drag gesture (#50925) --- packages/flutter/lib/src/material/drawer.dart | 34 +++-- .../flutter/lib/src/material/scaffold.dart | 36 +++-- .../flutter/test/material/scaffold_test.dart | 136 ++++++++++++++++++ 3 files changed, 186 insertions(+), 20 deletions(-) diff --git a/packages/flutter/lib/src/material/drawer.dart b/packages/flutter/lib/src/material/drawer.dart index 69058e2ec7..b23d649210 100644 --- a/packages/flutter/lib/src/material/drawer.dart +++ b/packages/flutter/lib/src/material/drawer.dart @@ -232,6 +232,7 @@ class DrawerController extends StatefulWidget { this.dragStartBehavior = DragStartBehavior.start, this.scrimColor, this.edgeDragWidth, + this.enableOpenDragGesture = true, }) : assert(child != null), assert(dragStartBehavior != null), assert(alignment != null), @@ -278,6 +279,11 @@ class DrawerController extends StatefulWidget { /// By default, the color used is [Colors.black54] final Color scrimColor; + /// Determines if the [Drawer] can be opened with a drag gesture. + /// + /// By default, the drag gesture is enabled. + final bool enableOpenDragGesture; + /// The width of the area within which a horizontal swipe will open the /// drawer. /// @@ -505,18 +511,22 @@ class DrawerControllerState extends State with SingleTickerPro } if (_controller.status == AnimationStatus.dismissed) { - return Align( - alignment: _drawerOuterAlignment, - child: GestureDetector( - key: _gestureDetectorKey, - onHorizontalDragUpdate: _move, - onHorizontalDragEnd: _settle, - behavior: HitTestBehavior.translucent, - excludeFromSemantics: true, - dragStartBehavior: widget.dragStartBehavior, - child: Container(width: dragAreaWidth), - ), - ); + if (widget.enableOpenDragGesture) { + return Align( + alignment: _drawerOuterAlignment, + child: GestureDetector( + key: _gestureDetectorKey, + onHorizontalDragUpdate: _move, + onHorizontalDragEnd: _settle, + behavior: HitTestBehavior.translucent, + excludeFromSemantics: true, + dragStartBehavior: widget.dragStartBehavior, + child: Container(width: dragAreaWidth), + ), + ); + } else { + return const SizedBox.shrink(); + } } else { bool platformHasBackButton; switch (Theme.of(context).platform) { diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index 3c1ef292ec..3d594cb5cb 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -1011,6 +1011,8 @@ class Scaffold extends StatefulWidget { this.extendBodyBehindAppBar = false, this.drawerScrimColor, this.drawerEdgeDragWidth, + this.drawerEnableOpenDragGesture = true, + this.endDrawerEnableOpenDragGesture = true, }) : assert(primary != null), assert(extendBody != null), assert(extendBodyBehindAppBar != null), @@ -1113,9 +1115,10 @@ class Scaffold extends StatefulWidget { /// To close the drawer, use [Navigator.pop]. /// /// {@tool dartpad --template=stateful_widget_material} - /// To disable the drawer edge swipe, set the [Scaffold.drawerEdgeWidth] to 0. - /// Then, use [ScaffoldState.openDrawer] to open the drawer and - /// [Navigator.pop] to close it. + /// To disable the drawer edge swipe, set the + /// [Scaffold.drawerEnableOpenDragGesture] to false. Then, use + /// [ScaffoldState.openDrawer] to open the drawer and [Navigator.pop] to close + /// it. /// /// ```dart /// final GlobalKey _scaffoldKey = GlobalKey(); @@ -1153,7 +1156,8 @@ class Scaffold extends StatefulWidget { /// ), /// ), /// ), - /// drawerEdgeDragWidth: 0.0, // Disable opening the drawer with a swipe gesture. + /// // Disable opening the drawer with a swipe gesture. + /// drawerEnableOpenDragGesture: false, /// ); /// } /// ``` @@ -1171,9 +1175,10 @@ class Scaffold extends StatefulWidget { /// To close the drawer, use [Navigator.pop]. /// /// {@tool dartpad --template=stateful_widget_material} - /// To disable the drawer edge swipe, set the [Scaffold.drawerEdgeWidth] - /// to 0. Then, use [ScaffoldState.openEndDrawer] to open the drawer and - /// [Navigator.pop] to close it. + /// To disable the drawer edge swipe, set the + /// [Scaffold.endDrawerEnableOpenDragGesture] to false. Then, use + /// [ScaffoldState.openEndDrawer] to open the drawer and [Navigator.pop] to + /// close it. /// /// ```dart /// final GlobalKey _scaffoldKey = GlobalKey(); @@ -1211,7 +1216,8 @@ class Scaffold extends StatefulWidget { /// ), /// ), /// ), - /// drawerEdgeDragWidth: 0.0, // Disable opening the drawer with a swipe gesture. + /// // Disable opening the end drawer with a swipe gesture. + /// endDrawerEnableOpenDragGesture: false, /// ); /// } /// ``` @@ -1313,6 +1319,18 @@ class Scaffold extends StatefulWidget { /// 20.0 will be added to `MediaQuery.of(context).padding.left`. final double drawerEdgeDragWidth; + /// Determines if the [Scaffold.drawer] can be opened with a drag + /// gesture. + /// + /// By default, the drag gesture is enabled. + final bool drawerEnableOpenDragGesture; + + /// Determines if the [Scaffold.endDrawer] can be opened with a + /// drag gesture. + /// + /// By default, the drag gesture is enabled. + final bool endDrawerEnableOpenDragGesture; + /// This flag is deprecated and fixes and issue with incorrect clipping /// and positioning of the [SnackBar] set to [SnackBarBehavior.floating]. @Deprecated( @@ -2230,6 +2248,7 @@ class ScaffoldState extends State with TickerProviderStateMixin { dragStartBehavior: widget.drawerDragStartBehavior, scrimColor: widget.drawerScrimColor, edgeDragWidth: widget.drawerEdgeDragWidth, + enableOpenDragGesture: widget.endDrawerEnableOpenDragGesture, ), _ScaffoldSlot.endDrawer, // remove the side padding from the side we're not touching @@ -2254,6 +2273,7 @@ class ScaffoldState extends State with TickerProviderStateMixin { dragStartBehavior: widget.drawerDragStartBehavior, scrimColor: widget.drawerScrimColor, edgeDragWidth: widget.drawerEdgeDragWidth, + enableOpenDragGesture: widget.drawerEnableOpenDragGesture, ), _ScaffoldSlot.drawer, // remove the side padding from the side we're not touching diff --git a/packages/flutter/test/material/scaffold_test.dart b/packages/flutter/test/material/scaffold_test.dart index 59170ef5e2..afcaebef25 100644 --- a/packages/flutter/test/material/scaffold_test.dart +++ b/packages/flutter/test/material/scaffold_test.dart @@ -1596,6 +1596,142 @@ void main() { expect(scaffoldState.isDrawerOpen, true); }); + testWidgets('Drawer does not open with a drag gesture when it is disabled', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + drawer: const Drawer( + child: Text('Drawer'), + ), + drawerEnableOpenDragGesture: true, + body: const Text('Scaffold Body'), + appBar: AppBar( + centerTitle: true, + title: const Text('Title'), + ), + ), + ), + ); + ScaffoldState scaffoldState = tester.state(find.byType(Scaffold)); + expect(scaffoldState.isDrawerOpen, false); + + // Test that we can open the drawer with a drag gesture when + // `Scaffold.drawerEnableDragGesture` is true. + await tester.dragFrom(const Offset(0, 100), const Offset(300, 0)); + await tester.pumpAndSettle(); + expect(scaffoldState.isDrawerOpen, true); + + await tester.dragFrom(const Offset(300, 100), const Offset(-300, 0)); + await tester.pumpAndSettle(); + expect(scaffoldState.isDrawerOpen, false); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + drawer: const Drawer( + child: Text('Drawer'), + ), + drawerEnableOpenDragGesture: false, + body: const Text('Scaffold body'), + appBar: AppBar( + centerTitle: true, + title: const Text('Title'), + ), + ), + ), + ); + scaffoldState = tester.state(find.byType(Scaffold)); + expect(scaffoldState.isDrawerOpen, false); + + // Test that we cannot open the drawer with a drag gesture when + // `Scaffold.drawerEnableDragGesture` is false. + await tester.dragFrom(const Offset(0, 100), const Offset(300, 0)); + await tester.pumpAndSettle(); + expect(scaffoldState.isDrawerOpen, false); + + // Test that we can close drawer with a drag gesture when + // `Scaffold.drawerEnableDragGesture` is false. + final Finder drawerOpenButton = find.byType(IconButton).first; + await tester.tap(drawerOpenButton); + await tester.pumpAndSettle(); + expect(scaffoldState.isDrawerOpen, true); + + await tester.dragFrom(const Offset(300, 100), const Offset(-300, 0)); + await tester.pumpAndSettle(); + expect(scaffoldState.isDrawerOpen, false); + }); + + testWidgets('End drawer does not open with a drag gesture when it is disabled', (WidgetTester tester) async { + double screenWidth; + await tester.pumpWidget( + MaterialApp( + home: Builder( + builder: (BuildContext context) { + screenWidth = MediaQuery.of(context).size.width; + return Scaffold( + endDrawer: const Drawer( + child: Text('Drawer'), + ), + endDrawerEnableOpenDragGesture: true, + body: const Text('Scaffold Body'), + appBar: AppBar( + centerTitle: true, + title: const Text('Title'), + ), + ); + } + ), + ), + ); + ScaffoldState scaffoldState = tester.state(find.byType(Scaffold)); + expect(scaffoldState.isEndDrawerOpen, false); + + // Test that we can open the end drawer with a drag gesture when + // `Scaffold.endDrawerEnableDragGesture` is true. + await tester.dragFrom(Offset(screenWidth - 1, 100), const Offset(-300, 0)); + await tester.pumpAndSettle(); + expect(scaffoldState.isEndDrawerOpen, true); + + await tester.dragFrom(Offset(screenWidth - 300, 100), const Offset(300, 0)); + await tester.pumpAndSettle(); + expect(scaffoldState.isEndDrawerOpen, false); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + endDrawer: const Drawer( + child: Text('Drawer'), + ), + endDrawerEnableOpenDragGesture: false, + body: const Text('Scaffold body'), + appBar: AppBar( + centerTitle: true, + title: const Text('Title'), + ), + ), + ), + ); + scaffoldState = tester.state(find.byType(Scaffold)); + expect(scaffoldState.isEndDrawerOpen, false); + + // Test that we cannot open the end drawer with a drag gesture when + // `Scaffold.endDrawerEnableDragGesture` is false. + await tester.dragFrom(Offset(screenWidth - 1, 100), const Offset(-300, 0)); + await tester.pumpAndSettle(); + expect(scaffoldState.isEndDrawerOpen, false); + + // Test that we can close the end drawer a with drag gesture when + // `Scaffold.endDrawerEnableDragGesture` is false. + final Finder endDrawerOpenButton = find.byType(IconButton).first; + await tester.tap(endDrawerOpenButton); + await tester.pumpAndSettle(); + expect(scaffoldState.isEndDrawerOpen, true); + + await tester.dragFrom(Offset(screenWidth - 300, 100), const Offset(300, 0)); + await tester.pumpAndSettle(); + expect(scaffoldState.isEndDrawerOpen, false); + }); + testWidgets('Nested scaffold body insets', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/20295 final Key bodyKey = UniqueKey();