Skip to content

Commit b2f72e0

Browse files
authored
Hides added routes before top-most route finishes pushing (#156104)
fixes flutter/flutter#156033
1 parent b01558c commit b2f72e0

File tree

2 files changed

+88
-22
lines changed

2 files changed

+88
-22
lines changed

packages/flutter/lib/src/widgets/navigator.dart

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ abstract class Route<T> extends _RoutePlaceholder {
225225
void _updateSettings(RouteSettings newSettings) {
226226
if (_settings != newSettings) {
227227
_settings = newSettings;
228-
changedInternalState();
228+
if (_navigator != null) {
229+
changedInternalState();
230+
}
229231
}
230232
}
231233

@@ -3101,10 +3103,6 @@ class _RouteEntry extends RouteTransitionRecord {
31013103
void handleAdd({ required NavigatorState navigator, required Route<dynamic>? previousPresent }) {
31023104
assert(currentState == _RouteLifecycle.add);
31033105
assert(navigator._debugLocked);
3104-
assert(route._navigator == null);
3105-
route._navigator = navigator;
3106-
route.install();
3107-
assert(route.overlayEntries.isNotEmpty);
31083106
currentState = _RouteLifecycle.adding;
31093107
navigator._observedRouteAdditions.add(
31103108
_NavigatorPushObservation(route, previousPresent),
@@ -3229,6 +3227,10 @@ class _RouteEntry extends RouteTransitionRecord {
32293227
}
32303228

32313229
void didAdd({ required NavigatorState navigator, required bool isNewFirst}) {
3230+
assert(route._navigator == null);
3231+
route._navigator = navigator;
3232+
route.install();
3233+
assert(route.overlayEntries.isNotEmpty);
32323234
route.didAdd();
32333235
currentState = _RouteLifecycle.idle;
32343236
if (isNewFirst) {
@@ -3825,7 +3827,9 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
38253827
super.didChangeDependencies();
38263828
_updateHeroController(HeroControllerScope.maybeOf(context));
38273829
for (final _RouteEntry entry in _history) {
3828-
entry.route.changedExternalState();
3830+
if (entry.route.navigator == this) {
3831+
entry.route.changedExternalState();
3832+
}
38293833
}
38303834
}
38313835

@@ -3945,7 +3949,9 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
39453949
}
39463950

39473951
for (final _RouteEntry entry in _history) {
3948-
entry.route.changedExternalState();
3952+
if (entry.route.navigator == this) {
3953+
entry.route.changedExternalState();
3954+
}
39493955
}
39503956
}
39513957

packages/flutter/test/widgets/navigator_test.dart

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3133,8 +3133,8 @@ void main() {
31333133
testWidgets('can push and pop pages using page api', (WidgetTester tester) async {
31343134
late Animation<double> secondaryAnimationOfRouteOne;
31353135
late Animation<double> primaryAnimationOfRouteOne;
3136-
late Animation<double> secondaryAnimationOfRouteTwo;
3137-
late Animation<double> primaryAnimationOfRouteTwo;
3136+
Animation<double>? secondaryAnimationOfRouteTwo;
3137+
Animation<double>? primaryAnimationOfRouteTwo;
31383138
late Animation<double> secondaryAnimationOfRouteThree;
31393139
late Animation<double> primaryAnimationOfRouteThree;
31403140
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
@@ -3208,24 +3208,24 @@ void main() {
32083208
// won't start until the third page finishes transition.
32093209
expect(secondaryAnimationOfRouteOne.value, primaryAnimationOfRouteThree.value);
32103210
expect(primaryAnimationOfRouteOne.status, AnimationStatus.completed);
3211-
expect(secondaryAnimationOfRouteTwo.status, AnimationStatus.dismissed);
3212-
expect(primaryAnimationOfRouteTwo.status, AnimationStatus.dismissed);
3211+
expect(secondaryAnimationOfRouteTwo, isNull);
3212+
expect(primaryAnimationOfRouteTwo, isNull);
32133213
expect(secondaryAnimationOfRouteThree.status, AnimationStatus.dismissed);
32143214
expect(primaryAnimationOfRouteThree.status, AnimationStatus.forward);
32153215

32163216
await tester.pump(const Duration(milliseconds: 30));
32173217
expect(secondaryAnimationOfRouteOne.value, primaryAnimationOfRouteThree.value);
32183218
expect(primaryAnimationOfRouteOne.status, AnimationStatus.completed);
3219-
expect(secondaryAnimationOfRouteTwo.status, AnimationStatus.dismissed);
3220-
expect(primaryAnimationOfRouteTwo.status, AnimationStatus.dismissed);
3219+
expect(secondaryAnimationOfRouteTwo, isNull);
3220+
expect(primaryAnimationOfRouteTwo, isNull);
32213221
expect(secondaryAnimationOfRouteThree.status, AnimationStatus.dismissed);
32223222
expect(primaryAnimationOfRouteThree.value, 0.1);
32233223
await tester.pumpAndSettle();
32243224
// After transition finishes, the routes' animations are correctly chained.
3225-
expect(secondaryAnimationOfRouteOne.value, primaryAnimationOfRouteTwo.value);
3225+
expect(secondaryAnimationOfRouteOne.value, primaryAnimationOfRouteTwo!.value);
32263226
expect(primaryAnimationOfRouteOne.status, AnimationStatus.completed);
3227-
expect(secondaryAnimationOfRouteTwo.value, primaryAnimationOfRouteThree.value);
3228-
expect(primaryAnimationOfRouteTwo.status, AnimationStatus.completed);
3227+
expect(secondaryAnimationOfRouteTwo!.value, primaryAnimationOfRouteThree.value);
3228+
expect(primaryAnimationOfRouteTwo!.status, AnimationStatus.completed);
32293229
expect(secondaryAnimationOfRouteThree.status, AnimationStatus.dismissed);
32303230
expect(primaryAnimationOfRouteThree.status, AnimationStatus.completed);
32313231
expect(find.text('third'), findsOneWidget);
@@ -3264,17 +3264,17 @@ void main() {
32643264
),
32653265
);
32663266
await tester.pump(const Duration(milliseconds: 30));
3267-
expect(secondaryAnimationOfRouteOne.value, primaryAnimationOfRouteTwo.value);
3267+
expect(secondaryAnimationOfRouteOne.value, primaryAnimationOfRouteTwo!.value);
32683268
expect(primaryAnimationOfRouteOne.status, AnimationStatus.completed);
3269-
expect(secondaryAnimationOfRouteTwo.value, primaryAnimationOfRouteThree.value);
3270-
expect(primaryAnimationOfRouteTwo.status, AnimationStatus.completed);
3269+
expect(secondaryAnimationOfRouteTwo!.value, primaryAnimationOfRouteThree.value);
3270+
expect(primaryAnimationOfRouteTwo!.status, AnimationStatus.completed);
32713271
expect(secondaryAnimationOfRouteThree.status, AnimationStatus.dismissed);
32723272
expect(primaryAnimationOfRouteThree.value, 0.9);
32733273
await tester.pumpAndSettle();
3274-
expect(secondaryAnimationOfRouteOne.value, primaryAnimationOfRouteTwo.value);
3274+
expect(secondaryAnimationOfRouteOne.value, primaryAnimationOfRouteTwo!.value);
32753275
expect(primaryAnimationOfRouteOne.status, AnimationStatus.completed);
3276-
expect(secondaryAnimationOfRouteTwo.value, primaryAnimationOfRouteThree.value);
3277-
expect(primaryAnimationOfRouteTwo.status, AnimationStatus.completed);
3276+
expect(secondaryAnimationOfRouteTwo!.value, primaryAnimationOfRouteThree.value);
3277+
expect(primaryAnimationOfRouteTwo!.status, AnimationStatus.completed);
32783278
expect(secondaryAnimationOfRouteThree.status, AnimationStatus.dismissed);
32793279
expect(primaryAnimationOfRouteThree.status, AnimationStatus.dismissed);
32803280
});
@@ -3415,6 +3415,66 @@ void main() {
34153415
expect(find.text('page1'), findsOneWidget);
34163416
});
34173417

3418+
testWidgets("Adds multiple pages won't suddenly jump before top most page finishes pushing", (WidgetTester tester) async {
3419+
// Regression Test for https://github.com/flutter/flutter/issues/156033.
3420+
List<Page<Object?>> pages = <Page<Object?>>[
3421+
MaterialPage<void>(
3422+
key: UniqueKey(),
3423+
child: const Text('home'),
3424+
),
3425+
];
3426+
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
3427+
Widget buildNavigator() {
3428+
return TestDependencies(
3429+
child: Navigator(
3430+
key: key,
3431+
pages: pages,
3432+
onDidRemovePage: (Page<void> page) {},
3433+
),
3434+
);
3435+
}
3436+
await tester.pumpWidget(buildNavigator());
3437+
expect(find.text('home'), findsOneWidget);
3438+
3439+
pages = <Page<Object?>>[
3440+
...pages,
3441+
MaterialPage<void>(
3442+
key: UniqueKey(),
3443+
child: const Text('child1'),
3444+
),
3445+
MaterialPage<void>(
3446+
key: UniqueKey(),
3447+
child: const Text('child2'),
3448+
),
3449+
];
3450+
3451+
await tester.pumpWidget(buildNavigator());
3452+
expect(find.text('home'), findsOneWidget);
3453+
// child 1 should not be created yet because the top-most route is
3454+
// still in transition.
3455+
expect(find.text('child1'), findsNothing);
3456+
expect(find.text('child2'), findsOneWidget);
3457+
3458+
await tester.pump(const Duration(milliseconds: 150));
3459+
expect(find.text('home'), findsOneWidget);
3460+
expect(find.text('child1'), findsNothing);
3461+
expect(find.text('child2'), findsOneWidget);
3462+
3463+
await tester.pumpAndSettle();
3464+
3465+
// Ensure child1 is indeed in the navigator.
3466+
key.currentState!.pop();
3467+
await tester.pumpAndSettle();
3468+
expect(find.text('home'), findsNothing);
3469+
expect(find.text('child1'), findsOneWidget);
3470+
expect(find.text('child2'), findsNothing);
3471+
3472+
key.currentState!.pop();
3473+
await tester.pumpAndSettle();
3474+
expect(find.text('home'), findsOneWidget);
3475+
expect(find.text('child1'), findsNothing);
3476+
});
3477+
34183478
testWidgets('can work with pageless route', (WidgetTester tester) async {
34193479
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
34203480
List<TestPage> myPages = <TestPage>[

0 commit comments

Comments
 (0)