Unverified Commit 8fe82bbe authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Add Scaffold drawers escape dismiss action. (#106186)

parent 1d5c189d
......@@ -111,8 +111,9 @@ const Duration _kBaseSettleDuration = Duration(milliseconds: 246);
/// ```
/// {@end-tool}
///
/// An open drawer can be closed by calling [Navigator.pop]. For example
/// a drawer item might close the drawer when tapped:
/// An open drawer may be closed with a swipe to close gesture, pressing the
/// the escape key, by tapping the scrim, or by calling pop route function such as
/// [Navigator.pop]. For example a drawer item might close the drawer when tapped:
///
/// ```dart
/// ListTile(
......
......@@ -1614,8 +1614,8 @@ class Scaffold extends StatefulWidget {
///
/// To open the drawer, use the [ScaffoldState.openDrawer] function.
///
/// To close the drawer, use either [ScaffoldState.closeDrawer] or
/// [Navigator.pop].
/// To close the drawer, use either [ScaffoldState.closeDrawer], [Navigator.pop]
/// or press the escape key on the keyboard.
///
/// {@tool dartpad}
/// To disable the drawer edge swipe on mobile, set the
......@@ -1638,8 +1638,8 @@ class Scaffold extends StatefulWidget {
///
/// To open the drawer, use the [ScaffoldState.openEndDrawer] function.
///
/// To close the drawer, use either [ScaffoldState.closeEndDrawer] or
/// [Navigator.pop].
/// To close the drawer, use either [ScaffoldState.closeEndDrawer], [Navigator.pop]
/// or press the escape key on the keyboard.
///
/// {@tool dartpad}
/// To disable the drawer edge swipe, set the
......@@ -2875,23 +2875,28 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
child: Material(
color: widget.backgroundColor ?? themeData.scaffoldBackgroundColor,
child: AnimatedBuilder(animation: _floatingActionButtonMoveController, builder: (BuildContext context, Widget? child) {
return CustomMultiChildLayout(
delegate: _ScaffoldLayout(
extendBody: extendBody,
extendBodyBehindAppBar: widget.extendBodyBehindAppBar,
minInsets: minInsets,
minViewPadding: minViewPadding,
currentFloatingActionButtonLocation: _floatingActionButtonLocation!,
floatingActionButtonMoveAnimationProgress: _floatingActionButtonMoveController.value,
floatingActionButtonMotionAnimator: _floatingActionButtonAnimator,
geometryNotifier: _geometryNotifier,
previousFloatingActionButtonLocation: _previousFloatingActionButtonLocation!,
textDirection: textDirection,
isSnackBarFloating: isSnackBarFloating,
extendBodyBehindMaterialBanner: extendBodyBehindMaterialBanner,
snackBarWidth: snackBarWidth,
return Actions(
actions: <Type, Action<Intent>>{
DismissIntent: _DismissDrawerAction(context),
},
child: CustomMultiChildLayout(
delegate: _ScaffoldLayout(
extendBody: extendBody,
extendBodyBehindAppBar: widget.extendBodyBehindAppBar,
minInsets: minInsets,
minViewPadding: minViewPadding,
currentFloatingActionButtonLocation: _floatingActionButtonLocation!,
floatingActionButtonMoveAnimationProgress: _floatingActionButtonMoveController.value,
floatingActionButtonMotionAnimator: _floatingActionButtonAnimator,
geometryNotifier: _geometryNotifier,
previousFloatingActionButtonLocation: _previousFloatingActionButtonLocation!,
textDirection: textDirection,
isSnackBarFloating: isSnackBarFloating,
extendBodyBehindMaterialBanner: extendBodyBehindMaterialBanner,
snackBarWidth: snackBarWidth,
),
children: children,
),
children: children,
);
}),
),
......@@ -2900,6 +2905,23 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
}
}
class _DismissDrawerAction extends DismissAction {
_DismissDrawerAction(this.context);
final BuildContext context;
@override
bool isEnabled(DismissIntent intent) {
return Scaffold.of(context).isDrawerOpen || Scaffold.of(context).isEndDrawerOpen;
}
@override
void invoke(DismissIntent intent) {
Scaffold.of(context).closeDrawer();
Scaffold.of(context).closeEndDrawer();
}
}
/// An interface for controlling a feature of a [Scaffold].
///
/// Commonly obtained from [ScaffoldMessengerState.showSnackBar] or
......
......@@ -350,6 +350,8 @@ void main() {
' MediaQuery\n'
' LayoutId-[<_ScaffoldSlot.snackBar>]\n'
' CustomMultiChildLayout\n'
' _ActionsMarker\n'
' Actions\n'
' AnimatedBuilder\n'
' DefaultTextStyle\n'
' AnimatedDefaultTextStyle\n'
......
......@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart';
......@@ -37,17 +38,17 @@ void main() {
scaffoldState.openDrawer();
await tester.pumpAndSettle();
expect(true, isDrawerOpen);
expect(isDrawerOpen, true);
scaffoldState.openEndDrawer();
await tester.pumpAndSettle();
expect(false, isDrawerOpen);
expect(isDrawerOpen, false);
scaffoldState.openEndDrawer();
await tester.pumpAndSettle();
expect(true, isEndDrawerOpen);
expect(isEndDrawerOpen, true);
scaffoldState.openDrawer();
await tester.pumpAndSettle();
expect(false, isEndDrawerOpen);
expect(isEndDrawerOpen, false);
});
testWidgets('Scaffold drawer callback test - only call when changed', (WidgetTester tester) async {
......@@ -74,14 +75,14 @@ void main() {
));
await tester.flingFrom(Offset.zero, const Offset(10.0, 0.0), 10.0);
expect(false, onDrawerChangedCalled);
expect(onDrawerChangedCalled, false);
await tester.pumpAndSettle();
final double width = tester.getSize(find.byType(MaterialApp)).width;
await tester.flingFrom(Offset(width - 1, 0.0), const Offset(-10.0, 0.0), 10.0);
await tester.pumpAndSettle();
expect(false, onEndDrawerChangedCalled);
expect(onEndDrawerChangedCalled, false);
});
testWidgets('Scaffold control test', (WidgetTester tester) async {
......@@ -1572,29 +1573,29 @@ void main() {
await tester.tap(drawerOpenButton);
await tester.pumpAndSettle();
expect(true, scaffoldState.isDrawerOpen);
expect(scaffoldState.isDrawerOpen, true);
await tester.tap(endDrawerOpenButton, warnIfMissed: false); // hits the modal barrier
await tester.pumpAndSettle();
expect(false, scaffoldState.isDrawerOpen);
expect(scaffoldState.isDrawerOpen, false);
await tester.tap(endDrawerOpenButton);
await tester.pumpAndSettle();
expect(true, scaffoldState.isEndDrawerOpen);
expect(scaffoldState.isEndDrawerOpen, true);
await tester.tap(drawerOpenButton, warnIfMissed: false); // hits the modal barrier
await tester.pumpAndSettle();
expect(false, scaffoldState.isEndDrawerOpen);
expect(scaffoldState.isEndDrawerOpen, false);
scaffoldState.openDrawer();
expect(true, scaffoldState.isDrawerOpen);
expect(scaffoldState.isDrawerOpen, true);
await tester.tap(endDrawerOpenButton, warnIfMissed: false); // hits the modal barrier
await tester.pumpAndSettle();
expect(false, scaffoldState.isDrawerOpen);
expect(scaffoldState.isDrawerOpen, false);
scaffoldState.openEndDrawer();
expect(true, scaffoldState.isEndDrawerOpen);
expect(scaffoldState.isEndDrawerOpen, true);
scaffoldState.openDrawer();
expect(true, scaffoldState.isDrawerOpen);
expect(scaffoldState.isDrawerOpen, true);
});
testWidgets('Dual Drawer Opening', (WidgetTester tester) async {
......@@ -2405,6 +2406,8 @@ void main() {
' MediaQuery\n'
' LayoutId-[<_ScaffoldSlot.body>]\n'
' CustomMultiChildLayout\n'
' _ActionsMarker\n'
' Actions\n'
' AnimatedBuilder\n'
' DefaultTextStyle\n'
' AnimatedDefaultTextStyle\n'
......@@ -2497,6 +2500,54 @@ void main() {
await tester.pumpAndSettle();
expect(find.text(snackBarContent), findsNothing);
});
testWidgets('Drawer can be dismissed with escape keyboard shortcut', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/106131
bool isDrawerOpen = false;
bool isEndDrawerOpen = false;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
drawer: Container(
color: Colors.blue,
),
onDrawerChanged: (bool isOpen) {
isDrawerOpen = isOpen;
},
endDrawer: Container(
color: Colors.green,
),
onEndDrawerChanged: (bool isOpen) {
isEndDrawerOpen = isOpen;
},
body: Container(),
),
));
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
scaffoldState.openDrawer();
await tester.pumpAndSettle();
expect(isDrawerOpen, true);
expect(isEndDrawerOpen, false);
// Try to dismiss the drawer with the shortcut key
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
await tester.pumpAndSettle();
expect(isDrawerOpen, false);
expect(isEndDrawerOpen, false);
scaffoldState.openEndDrawer();
await tester.pumpAndSettle();
expect(isDrawerOpen, false);
expect(isEndDrawerOpen, true);
// Try to dismiss the drawer with the shortcut key
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
await tester.pumpAndSettle();
expect(isDrawerOpen, false);
expect(isEndDrawerOpen, false);
});
}
class _GeometryListener extends StatefulWidget {
......
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