mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Handle back during pushReplacement (#10016)
Previously, we would strand the old route in the overlay even though it had been removed from the history.
This commit is contained in:
parent
b9f4e5dac7
commit
bd56c79767
|
@ -102,7 +102,10 @@ class TabController extends ChangeNotifier {
|
|||
notifyListeners(); // Because the value of indexIsChanging may have changed.
|
||||
_animationController
|
||||
.animateTo(_index.toDouble(), duration: duration, curve: curve)
|
||||
.orCancel.then<Null>(_indexChanged, onError: _indexChanged);
|
||||
.whenCompleteOrCancel(() {
|
||||
_indexIsChangingCount -= 1;
|
||||
notifyListeners();
|
||||
});
|
||||
} else {
|
||||
_indexIsChangingCount += 1;
|
||||
_animationController.value = _index.toDouble();
|
||||
|
@ -111,12 +114,6 @@ class TabController extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
Null _indexChanged(dynamic value) {
|
||||
_indexIsChangingCount -= 1;
|
||||
notifyListeners();
|
||||
return null;
|
||||
}
|
||||
|
||||
/// The index of the currently selected tab. Changing the index also updates
|
||||
/// [previousIndex], sets the [animation]'s value to index, resets
|
||||
/// [indexIsChanging] to false, and notifies listeners.
|
||||
|
|
|
@ -333,6 +333,9 @@ class Ticker {
|
|||
/// [orCancel], which returns a derivative [Future] that completes with an error
|
||||
/// if the [Ticker] that returned the [TickerFuture] was stopped with `canceled`
|
||||
/// set to true, or if it was disposed without being stopped.
|
||||
///
|
||||
/// To run a callback when either this future resolves or when the tricker is
|
||||
/// canceled, use [whenCompleteOrCancel].
|
||||
class TickerFuture implements Future<Null> {
|
||||
TickerFuture._();
|
||||
|
||||
|
@ -364,6 +367,16 @@ class TickerFuture implements Future<Null> {
|
|||
_secondaryCompleter?.completeError(new TickerCanceled(ticker));
|
||||
}
|
||||
|
||||
/// Calls `callback` either when this future resolves or when the ticker is
|
||||
/// canceled.
|
||||
void whenCompleteOrCancel(VoidCallback callback) {
|
||||
Null thunk(dynamic value) {
|
||||
callback();
|
||||
return null;
|
||||
}
|
||||
orCancel.then(thunk, onError: thunk);
|
||||
}
|
||||
|
||||
/// A future that resolves when this future resolves or throws when the ticker
|
||||
/// is canceled.
|
||||
Future<Null> get orCancel {
|
||||
|
|
|
@ -58,7 +58,7 @@ abstract class Route<T> {
|
|||
///
|
||||
/// The returned value resolves when the push transition is complete.
|
||||
@protected
|
||||
Future<Null> didPush() => new Future<Null>.value();
|
||||
TickerFuture didPush() => new TickerFuture.complete();
|
||||
|
||||
/// When this route is popped (see [Navigator.pop]) if the result isn't
|
||||
/// specified or if it's null, this value will be used instead.
|
||||
|
@ -894,7 +894,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
|
|||
newRoute._navigator = this;
|
||||
newRoute.install(_currentOverlayEntry);
|
||||
_history[index] = newRoute;
|
||||
newRoute.didPush().then<Null>((Null value) {
|
||||
newRoute.didPush().whenCompleteOrCancel(() {
|
||||
// The old route's exit is not animated. We're assuming that the
|
||||
// new route completely obscures the old one.
|
||||
if (mounted) {
|
||||
|
|
|
@ -165,7 +165,7 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Null> didPush() {
|
||||
TickerFuture didPush() {
|
||||
_animation.addStatusListener(_handleStatusChanged);
|
||||
return _controller.forward();
|
||||
}
|
||||
|
@ -704,7 +704,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Null> didPush() {
|
||||
TickerFuture didPush() {
|
||||
navigator.focusScopeNode.setFirstFocus(focusScopeNode);
|
||||
return super.didPush();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Back during pushReplacement',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(new MaterialApp(
|
||||
home: const Material(child: const Text("home")),
|
||||
routes: <String, WidgetBuilder> {
|
||||
'/a': (BuildContext context) => const Material(child: const Text("a")),
|
||||
'/b': (BuildContext context) => const Material(child: const Text("b")),
|
||||
},
|
||||
));
|
||||
|
||||
final NavigatorState navigator = tester.state(find.byType(Navigator));
|
||||
navigator.pushNamed('/a');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('a'), findsOneWidget);
|
||||
expect(find.text('home'), findsNothing);
|
||||
|
||||
navigator.pushReplacementNamed('/b');
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 10));
|
||||
|
||||
expect(find.text('a'), findsOneWidget);
|
||||
expect(find.text('b'), findsOneWidget);
|
||||
expect(find.text('home'), findsNothing);
|
||||
|
||||
navigator.pop();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('a'), findsNothing);
|
||||
expect(find.text('b'), findsNothing);
|
||||
expect(find.text('home'), findsOneWidget);
|
||||
});
|
||||
}
|
|
@ -38,7 +38,7 @@ class TestRoute extends LocalHistoryRoute<String> {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Null> didPush() {
|
||||
TickerFuture didPush() {
|
||||
log('didPush');
|
||||
return super.didPush();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue