Skip to content

Commit 1718519

Browse files
authored
Fix drawers are draggable on desktop platforms (#100476)
1 parent bd68306 commit 1718519

File tree

3 files changed

+107
-45
lines changed

3 files changed

+107
-45
lines changed

packages/flutter/lib/src/material/drawer.dart

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,19 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
565565
final bool drawerIsStart = widget.alignment == DrawerAlignment.start;
566566
final EdgeInsets padding = MediaQuery.of(context).padding;
567567
final TextDirection textDirection = Directionality.of(context);
568+
final bool isDesktop;
569+
switch (Theme.of(context).platform) {
570+
case TargetPlatform.android:
571+
case TargetPlatform.iOS:
572+
case TargetPlatform.fuchsia:
573+
isDesktop = false;
574+
break;
575+
case TargetPlatform.macOS:
576+
case TargetPlatform.linux:
577+
case TargetPlatform.windows:
578+
isDesktop = true;
579+
break;
580+
}
568581

569582
double? dragAreaWidth = widget.edgeDragWidth;
570583
if (widget.edgeDragWidth == null) {
@@ -581,7 +594,7 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
581594
}
582595

583596
if (_controller.status == AnimationStatus.dismissed) {
584-
if (widget.enableOpenDragGesture) {
597+
if (widget.enableOpenDragGesture && !isDesktop) {
585598
return Align(
586599
alignment: _drawerOuterAlignment,
587600
child: GestureDetector(
@@ -612,52 +625,57 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
612625
break;
613626
}
614627
assert(platformHasBackButton != null);
615-
return GestureDetector(
616-
key: _gestureDetectorKey,
617-
onHorizontalDragDown: _handleDragDown,
618-
onHorizontalDragUpdate: _move,
619-
onHorizontalDragEnd: _settle,
620-
onHorizontalDragCancel: _handleDragCancel,
621-
excludeFromSemantics: true,
622-
dragStartBehavior: widget.dragStartBehavior,
623-
child: RepaintBoundary(
624-
child: Stack(
625-
children: <Widget>[
626-
BlockSemantics(
627-
child: ExcludeSemantics(
628-
// On Android, the back button is used to dismiss a modal.
629-
excluding: platformHasBackButton,
630-
child: GestureDetector(
631-
onTap: close,
632-
child: Semantics(
633-
label: MaterialLocalizations.of(context).modalBarrierDismissLabel,
634-
child: MouseRegion(
635-
child: Container( // The drawer's "scrim"
636-
color: _scrimColorTween.evaluate(_controller),
637-
),
638-
),
628+
629+
final Widget child = RepaintBoundary(
630+
child: Stack(
631+
children: <Widget>[
632+
BlockSemantics(
633+
child: ExcludeSemantics(
634+
// On Android, the back button is used to dismiss a modal.
635+
excluding: platformHasBackButton,
636+
child: GestureDetector(
637+
onTap: close,
638+
child: Semantics(
639+
label: MaterialLocalizations.of(context).modalBarrierDismissLabel,
640+
child: Container( // The drawer's "scrim"
641+
color: _scrimColorTween.evaluate(_controller),
639642
),
640643
),
641644
),
642645
),
643-
Align(
644-
alignment: _drawerOuterAlignment,
645-
child: Align(
646-
alignment: _drawerInnerAlignment,
647-
widthFactor: _controller.value,
648-
child: RepaintBoundary(
649-
child: FocusScope(
650-
key: _drawerKey,
651-
node: _focusScopeNode,
652-
child: widget.child,
653-
),
646+
),
647+
Align(
648+
alignment: _drawerOuterAlignment,
649+
child: Align(
650+
alignment: _drawerInnerAlignment,
651+
widthFactor: _controller.value,
652+
child: RepaintBoundary(
653+
child: FocusScope(
654+
key: _drawerKey,
655+
node: _focusScopeNode,
656+
child: widget.child,
654657
),
655658
),
656659
),
657-
],
658-
),
660+
),
661+
],
659662
),
660663
);
664+
665+
if (isDesktop) {
666+
return child;
667+
}
668+
669+
return GestureDetector(
670+
key: _gestureDetectorKey,
671+
onHorizontalDragDown: _handleDragDown,
672+
onHorizontalDragUpdate: _move,
673+
onHorizontalDragEnd: _settle,
674+
onHorizontalDragCancel: _handleDragCancel,
675+
excludeFromSemantics: true,
676+
dragStartBehavior: widget.dragStartBehavior,
677+
child: child,
678+
);
661679
}
662680
}
663681

packages/flutter/lib/src/material/scaffold.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,7 +1617,7 @@ class Scaffold extends StatefulWidget {
16171617
/// [Navigator.pop].
16181618
///
16191619
/// {@tool dartpad}
1620-
/// To disable the drawer edge swipe, set the
1620+
/// To disable the drawer edge swipe on mobile, set the
16211621
/// [Scaffold.drawerEnableOpenDragGesture] to false. Then, use
16221622
/// [ScaffoldState.openDrawer] to open the drawer and [Navigator.pop] to close
16231623
/// it.
@@ -1739,15 +1739,19 @@ class Scaffold extends StatefulWidget {
17391739
final double? drawerEdgeDragWidth;
17401740

17411741
/// Determines if the [Scaffold.drawer] can be opened with a drag
1742-
/// gesture.
1742+
/// gesture on mobile.
1743+
///
1744+
/// On desktop platforms, the drawer is not draggable.
17431745
///
1744-
/// By default, the drag gesture is enabled.
1746+
/// By default, the drag gesture is enabled on mobile.
17451747
final bool drawerEnableOpenDragGesture;
17461748

17471749
/// Determines if the [Scaffold.endDrawer] can be opened with a
1748-
/// drag gesture.
1750+
/// gesture on mobile.
1751+
///
1752+
/// On desktop platforms, the drawer is not draggable.
17491753
///
1750-
/// By default, the drag gesture is enabled.
1754+
/// By default, the drag gesture is enabled on mobile.
17511755
final bool endDrawerEnableOpenDragGesture;
17521756

17531757
/// Restoration ID to save and restore the state of the [Scaffold].

packages/flutter/test/material/scaffold_test.dart

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1777,7 +1777,7 @@ void main() {
17771777
expect(scaffoldState.isDrawerOpen, true);
17781778
});
17791779

1780-
testWidgets('Drawer does not open with a drag gesture when it is disabled', (WidgetTester tester) async {
1780+
testWidgets('Drawer does not open with a drag gesture when it is disabled on mobile', (WidgetTester tester) async {
17811781
await tester.pumpWidget(
17821782
MaterialApp(
17831783
home: Scaffold(
@@ -1839,7 +1839,47 @@ void main() {
18391839
await tester.dragFrom(const Offset(300, 100), const Offset(-300, 0));
18401840
await tester.pumpAndSettle();
18411841
expect(scaffoldState.isDrawerOpen, false);
1842-
});
1842+
}, variant: TargetPlatformVariant.mobile());
1843+
1844+
testWidgets('Drawer does not open with a drag gesture on dekstop', (WidgetTester tester) async {
1845+
await tester.pumpWidget(
1846+
MaterialApp(
1847+
home: Scaffold(
1848+
drawer: const Drawer(
1849+
child: Text('Drawer'),
1850+
),
1851+
body: const Text('Scaffold Body'),
1852+
appBar: AppBar(
1853+
centerTitle: true,
1854+
title: const Text('Title'),
1855+
),
1856+
),
1857+
),
1858+
);
1859+
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
1860+
expect(scaffoldState.isDrawerOpen, false);
1861+
1862+
// Test that we cannot open the drawer with a drag gesture.
1863+
await tester.dragFrom(const Offset(0, 100), const Offset(300, 0));
1864+
await tester.pumpAndSettle();
1865+
expect(scaffoldState.isDrawerOpen, false);
1866+
1867+
// Test that we can open the drawer with a tap gesture on drawer icon button.
1868+
final Finder drawerOpenButton = find.byType(IconButton).first;
1869+
await tester.tap(drawerOpenButton);
1870+
await tester.pumpAndSettle();
1871+
expect(scaffoldState.isDrawerOpen, true);
1872+
1873+
// Test that we cannot close the drawer with a drag gesture.
1874+
await tester.dragFrom(const Offset(300, 100), const Offset(-300, 0));
1875+
await tester.pumpAndSettle();
1876+
expect(scaffoldState.isDrawerOpen, true);
1877+
1878+
// Test that we can close the drawer with a tap gesture in the body.
1879+
await tester.tapAt(const Offset(500, 300));
1880+
await tester.pumpAndSettle();
1881+
expect(scaffoldState.isDrawerOpen, false);
1882+
}, variant: TargetPlatformVariant.desktop());
18431883

18441884
testWidgets('End drawer does not open with a drag gesture when it is disabled', (WidgetTester tester) async {
18451885
late double screenWidth;

0 commit comments

Comments
 (0)