From b23105276f65f4686a29d29255de86a7527cee72 Mon Sep 17 00:00:00 2001 From: Bruno Leroux Date: Wed, 30 Aug 2023 21:02:06 +0200 Subject: [PATCH] Expose barrierDismissible in PageRoute constructor (#133659) ## Description This PR exposes `barrierDismissible` in `PageRoute`, `MaterialPageRoute` and `CupertinoPageRoute` constructors. Setting this property to true will enable the escape key binding. ## Related Issue Fixes https://github.com/flutter/flutter/issues/132138. ## Tests Adds one test. --- packages/flutter/lib/src/cupertino/route.dart | 4 ++ packages/flutter/lib/src/material/page.dart | 4 ++ packages/flutter/lib/src/widgets/pages.dart | 9 ++++- packages/flutter/test/material/page_test.dart | 40 +++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart index 211578214871..c53efc7e87ea 100644 --- a/packages/flutter/lib/src/cupertino/route.dart +++ b/packages/flutter/lib/src/cupertino/route.dart @@ -311,6 +311,9 @@ mixin CupertinoRouteTransitionMixin on PageRoute { /// the route is popped from the stack via [Navigator.pop] when an optional /// `result` can be provided. /// +/// If `barrierDismissible` is true, then pressing the escape key on the keyboard +/// will cause the current route to be popped with null as the value. +/// /// See also: /// /// * [CupertinoRouteTransitionMixin], for a mixin that provides iOS transition @@ -334,6 +337,7 @@ class CupertinoPageRoute extends PageRoute with CupertinoRouteTransitionMi this.maintainState = true, super.fullscreenDialog, super.allowSnapshotting = true, + super.barrierDismissible = false, }) { assert(opaque); } diff --git a/packages/flutter/lib/src/material/page.dart b/packages/flutter/lib/src/material/page.dart index 73d677ff4cd7..0c1be0c83a2d 100644 --- a/packages/flutter/lib/src/material/page.dart +++ b/packages/flutter/lib/src/material/page.dart @@ -20,6 +20,9 @@ import 'theme.dart'; /// fullscreen modal dialog. On iOS, those routes animate from the bottom to the /// top rather than horizontally. /// +/// If `barrierDismissible` is true, then pressing the escape key on the keyboard +/// will cause the current route to be popped with null as the value. +/// /// The type `T` specifies the return type of the route which can be supplied as /// the route is popped from the stack via [Navigator.pop] by providing the /// optional `result` argument. @@ -40,6 +43,7 @@ class MaterialPageRoute extends PageRoute with MaterialRouteTransitionMixi this.maintainState = true, super.fullscreenDialog, super.allowSnapshotting = true, + super.barrierDismissible = false, }) { assert(opaque); } diff --git a/packages/flutter/lib/src/widgets/pages.dart b/packages/flutter/lib/src/widgets/pages.dart index de3ff66f9439..989beb1f4283 100644 --- a/packages/flutter/lib/src/widgets/pages.dart +++ b/packages/flutter/lib/src/widgets/pages.dart @@ -11,6 +11,9 @@ import 'routes.dart'; /// The [PageRouteBuilder] subclass provides a way to create a [PageRoute] using /// callbacks rather than by defining a new class via subclassing. /// +/// If `barrierDismissible` is true, then pressing the escape key on the keyboard +/// will cause the current route to be popped with null as the value. +/// /// See also: /// /// * [Route], which documents the meaning of the `T` generic type argument. @@ -20,7 +23,8 @@ abstract class PageRoute extends ModalRoute { super.settings, this.fullscreenDialog = false, this.allowSnapshotting = true, - }); + bool barrierDismissible = false, + }) : _barrierDismissible = barrierDismissible; /// {@template flutter.widgets.PageRoute.fullscreenDialog} /// Whether this page route is a full-screen dialog. @@ -39,7 +43,8 @@ abstract class PageRoute extends ModalRoute { bool get opaque => true; @override - bool get barrierDismissible => false; + bool get barrierDismissible => _barrierDismissible; + final bool _barrierDismissible; @override bool canTransitionTo(TransitionRoute nextRoute) => nextRoute is PageRoute; diff --git a/packages/flutter/test/material/page_test.dart b/packages/flutter/test/material/page_test.dart index 6f6728af70ef..d353803e779e 100644 --- a/packages/flutter/test/material/page_test.dart +++ b/packages/flutter/test/material/page_test.dart @@ -11,6 +11,7 @@ import 'package:flutter/cupertino.dart' show CupertinoPageRoute; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -1229,6 +1230,45 @@ void main() { expect(find.text('p1'), findsOneWidget); expect(find.text('count: 1'), findsOneWidget); }); + + testWidgets('MaterialPageRoute can be dismissed with escape keyboard shortcut', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/132138. + final GlobalKey scaffoldKey = GlobalKey(); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + key: scaffoldKey, + body: Center( + child: ElevatedButton( + onPressed: () { + Navigator.push(scaffoldKey.currentContext!, MaterialPageRoute( + builder: (BuildContext context) { + return const Scaffold( + body: Center(child: Text('route')), + ); + }, + barrierDismissible: true, + )); + }, + child: const Text('push'), + ), + ), + ), + ), + ); + + await tester.tap(find.text('push')); + await tester.pumpAndSettle(); + expect(find.text('route'), findsOneWidget); + expect(find.text('push'), findsNothing); + + // Try to dismiss the route with the escape key. + await tester.sendKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + expect(find.text('route'), findsNothing); + }); } class TransitionDetector extends DefaultTransitionDelegate {