Commit 6d433341 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Don't try to pop the initial route (#8307)

This functionality got lots somehow in the migration to maybePop.

Fixes #8258
parent 862fc051
...@@ -312,7 +312,7 @@ class _AppBarState extends State<AppBar> { ...@@ -312,7 +312,7 @@ class _AppBarState extends State<AppBar> {
super.dependenciesChanged(); super.dependenciesChanged();
ScaffoldState scaffold = Scaffold.of(context); ScaffoldState scaffold = Scaffold.of(context);
_hasDrawer = scaffold?.hasDrawer ?? false; _hasDrawer = scaffold?.hasDrawer ?? false;
_canPop = ModalRoute.of(context)?.canPop() ?? false; _canPop = ModalRoute.of(context)?.canPop ?? false;
} }
void _handleDrawerButton() { void _handleDrawerButton() {
......
...@@ -70,10 +70,14 @@ abstract class Route<T> { ...@@ -70,10 +70,14 @@ abstract class Route<T> {
/// Returns false if this route wants to veto a [Navigator.pop]. This method is /// Returns false if this route wants to veto a [Navigator.pop]. This method is
/// called by [Naviagtor.willPop]. /// called by [Naviagtor.willPop].
/// ///
/// By default, routes veto a pop if they're the first route in the history
/// (i.e., if [isFirst]). This behavior prevents the user from popping the
/// first route off the history and being stranded at a blank screen.
///
/// See also: /// See also:
/// ///
/// * [Form], which provides an `onWillPop` callback that uses this mechanism. /// * [Form], which provides an `onWillPop` callback that uses this mechanism.
Future<bool> willPop() async => true; Future<bool> willPop() async => !isFirst;
/// A request was made to pop this route. If the route can handle it /// A request was made to pop this route. If the route can handle it
/// internally (e.g. because it has its own stack of internal state) then /// internally (e.g. because it has its own stack of internal state) then
......
...@@ -315,6 +315,11 @@ abstract class LocalHistoryRoute<T> extends Route<T> { ...@@ -315,6 +315,11 @@ abstract class LocalHistoryRoute<T> extends Route<T> {
changedInternalState(); changedInternalState();
} }
@override
Future<bool> willPop() async {
return willHandlePopInternally || await super.willPop();
}
@override @override
bool didPop(T result) { bool didPop(T result) {
if (_localHistory != null && _localHistory.isNotEmpty) { if (_localHistory != null && _localHistory.isNotEmpty) {
...@@ -401,7 +406,7 @@ class _ModalScope extends StatefulWidget { ...@@ -401,7 +406,7 @@ class _ModalScope extends StatefulWidget {
class _ModalScopeState extends State<_ModalScope> { class _ModalScopeState extends State<_ModalScope> {
// See addScopedWillPopCallback, removeScopedWillPopCallback in ModalRoute. // See addScopedWillPopCallback, removeScopedWillPopCallback in ModalRoute.
List<WillPopCallback> willPopCallbacks = <WillPopCallback>[]; final List<WillPopCallback> _willPopCallbacks = <WillPopCallback>[];
@override @override
void initState() { void initState() {
...@@ -424,12 +429,12 @@ class _ModalScopeState extends State<_ModalScope> { ...@@ -424,12 +429,12 @@ class _ModalScopeState extends State<_ModalScope> {
void addWillPopCallback(WillPopCallback callback) { void addWillPopCallback(WillPopCallback callback) {
assert(mounted); assert(mounted);
willPopCallbacks.add(callback); _willPopCallbacks.add(callback);
} }
void removeWillPopCallback(WillPopCallback callback) { void removeWillPopCallback(WillPopCallback callback) {
assert(mounted); assert(mounted);
willPopCallbacks.remove(callback); _willPopCallbacks.remove(callback);
} }
void _animationStatusChanged(AnimationStatus status) { void _animationStatusChanged(AnimationStatus status) {
...@@ -461,7 +466,7 @@ class _ModalScopeState extends State<_ModalScope> { ...@@ -461,7 +466,7 @@ class _ModalScopeState extends State<_ModalScope> {
child: new _ModalScopeStatus( child: new _ModalScopeStatus(
route: config.route, route: config.route,
isCurrent: config.route.isCurrent, isCurrent: config.route.isCurrent,
canPop: config.route.canPop(), canPop: config.route.canPop,
child: config.page, child: config.page,
), ),
), ),
...@@ -655,7 +660,8 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -655,7 +660,8 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
ProxyAnimation _forwardAnimationProxy; ProxyAnimation _forwardAnimationProxy;
/// Return the value of the first callback added with /// Return the value of the first callback added with
/// [addScopedWillPopCallback] that returns false. Otherwise return true. /// [addScopedWillPopCallback] that returns false. Otherwise return
/// [super.willPop()].
/// ///
/// Typically this method is not overridden because applications usually /// Typically this method is not overridden because applications usually
/// don't create modal routes directly, they use higher level primitives /// don't create modal routes directly, they use higher level primitives
...@@ -673,11 +679,11 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -673,11 +679,11 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
Future<bool> willPop() async { Future<bool> willPop() async {
final _ModalScopeState scope = _scopeKey.currentState; final _ModalScopeState scope = _scopeKey.currentState;
assert(scope != null); assert(scope != null);
for (WillPopCallback callback in new List<WillPopCallback>.from(scope.willPopCallbacks)) { for (WillPopCallback callback in new List<WillPopCallback>.from(scope._willPopCallbacks)) {
if (!await callback()) if (!await callback())
return false; return false;
} }
return true; return await super.willPop();
} }
/// Enables this route to veto attempts by the user to dismiss it. /// Enables this route to veto attempts by the user to dismiss it.
...@@ -754,7 +760,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -754,7 +760,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
/// * [removeScopedWillPopCallback], which removes a callback. /// * [removeScopedWillPopCallback], which removes a callback.
@protected @protected
bool get hasScopedWillPopCallback { bool get hasScopedWillPopCallback {
return _scopeKey.currentState == null || _scopeKey.currentState.willPopCallbacks.length > 0; return _scopeKey.currentState == null || _scopeKey.currentState._willPopCallbacks.length > 0;
} }
@override @override
...@@ -773,7 +779,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -773,7 +779,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
/// ///
/// When this changes, the route will rebuild, and any widgets that used /// When this changes, the route will rebuild, and any widgets that used
/// [ModalRoute.of] will be notified. /// [ModalRoute.of] will be notified.
bool canPop() => !isFirst || willHandlePopInternally; bool get canPop => !isFirst || willHandlePopInternally;
// Internals // Internals
......
...@@ -117,4 +117,16 @@ void main() { ...@@ -117,4 +117,16 @@ void main() {
expect(buildCounter, 2); expect(buildCounter, 2);
}); });
testWidgets('Cannot pop the initial route', (WidgetTester tester) async {
await tester.pumpWidget(new MaterialApp(home: new Text('Home')));
expect(find.text('Home'), findsOneWidget);
NavigatorState navigator = tester.state(find.byType(Navigator));
bool result = await navigator.maybePop();
expect(result, isFalse);
expect(find.text('Home'), findsOneWidget);
});
} }
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