| // Copyright 2014 The Flutter 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/foundation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| void main() { |
| testWidgets('Restoration Smoke Test', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| |
| expect(findRoute('home', count: 0), findsOneWidget); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 2), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 3), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| expect(findRoute('home', count: 2), findsOneWidget); |
| }); |
| |
| testWidgets('restorablePushNamed', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo', arguments: 3); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Foo', count: 0, arguments: 3), findsOneWidget); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2, arguments: 3), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar', arguments: 4); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar', arguments: 4), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| expect(findRoute('Bar'), findsNothing); |
| }); |
| |
| testWidgets('restorablePushReplacementNamed', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushReplacementNamed('Foo', arguments: 3); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0, arguments: 3), findsOneWidget); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2, arguments: 3), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar', arguments: 4); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar', arguments: 4), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| expect(findRoute('Bar'), findsNothing); |
| }); |
| |
| testWidgets('restorablePopAndPushNamed', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePopAndPushNamed('Foo', arguments: 3); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0, arguments: 3), findsOneWidget); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2, arguments: 3), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar', arguments: 4); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar', arguments: 4), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| expect(findRoute('Bar'), findsNothing); |
| }); |
| |
| testWidgets('restorablePushNamedAndRemoveUntil', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamedAndRemoveUntil('Foo', (Route<dynamic> _) => false, arguments: 3); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0, arguments: 3), findsOneWidget); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2, arguments: 3), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar', arguments: 4); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar', arguments: 4), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1, arguments: 3), findsOneWidget); |
| expect(findRoute('Bar'), findsNothing); |
| }); |
| |
| testWidgets('restorablePush', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePush(_routeBuilder, arguments: 'Foo'); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Foo', count: 0), findsOneWidget); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar'), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| expect(findRoute('Bar'), findsNothing); |
| }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 |
| |
| testWidgets('restorablePush adds route on all platforms', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePush(_routeBuilder, arguments: 'Foo'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo'), findsOneWidget); |
| }); |
| |
| testWidgets('restorablePushReplacement', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushReplacement(_routeBuilder, arguments: 'Foo'); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0), findsOneWidget); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar'), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| expect(findRoute('Bar'), findsNothing); |
| }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 |
| |
| testWidgets('restorablePushReplacement adds route on all platforms', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushReplacement(_routeBuilder, arguments: 'Foo'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo'), findsOneWidget); |
| }); |
| |
| testWidgets('restorablePushAndRemoveUntil', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushAndRemoveUntil(_routeBuilder, (Route<dynamic> _) => false, arguments: 'Foo'); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0), findsOneWidget); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar'), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| expect(findRoute('Bar'), findsNothing); |
| }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 |
| |
| testWidgets('restorablePushAndRemoveUntil adds route on all platforms', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushAndRemoveUntil(_routeBuilder, (Route<dynamic> _) => false, arguments: 'Foo'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo'), findsOneWidget); |
| }); |
| |
| testWidgets('restorableReplace', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| |
| final Route<Object> oldRoute = ModalRoute.of(tester.element(find.text('Route: home')))!; |
| expect(oldRoute.settings.name, 'home'); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorableReplace(newRouteBuilder: _routeBuilder, arguments: 'Foo', oldRoute: oldRoute); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0), findsOneWidget); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar'), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 1), findsOneWidget); |
| expect(findRoute('Bar'), findsNothing); |
| }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 |
| |
| testWidgets('restorableReplace adds route on all platforms', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| |
| final Route<Object> oldRoute = ModalRoute.of(tester.element(find.text('Route: home')))!; |
| expect(oldRoute.settings.name, 'home'); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorableReplace(newRouteBuilder: _routeBuilder, arguments: 'Foo', oldRoute: oldRoute); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo'), findsOneWidget); |
| }); |
| |
| testWidgets('restorableReplaceRouteBelow', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Anchor'); |
| await tester.pumpAndSettle(); |
| |
| await tapRouteCounter('Anchor', tester); |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 0, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Anchor', count: 1), findsOneWidget); |
| |
| final Route<Object> anchor = ModalRoute.of(tester.element(find.text('Route: Anchor')))!; |
| expect(anchor.settings.name, 'Anchor'); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorableReplaceRouteBelow(newRouteBuilder: _routeBuilder, arguments: 'Foo', anchorRoute: anchor); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Anchor', count: 1), findsOneWidget); |
| |
| await tapRouteCounter('Anchor', tester); |
| expect(findRoute('Anchor', count: 2), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Anchor', count: 2), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Anchor', tester); |
| expect(findRoute('Anchor', count: 3), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar'), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 0, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Anchor', count: 2), findsOneWidget); |
| }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 |
| |
| testWidgets('restorableReplaceRouteBelow adds route on all platforms', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Anchor'); |
| await tester.pumpAndSettle(); |
| |
| await tapRouteCounter('Anchor', tester); |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 0, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Anchor', count: 1), findsOneWidget); |
| |
| final Route<Object> anchor = ModalRoute.of(tester.element(find.text('Route: Anchor')))!; |
| expect(anchor.settings.name, 'Anchor'); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorableReplaceRouteBelow(newRouteBuilder: _routeBuilder, arguments: 'Foo', anchorRoute: anchor); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo', skipOffstage: false), findsOneWidget); |
| }); |
| |
| testWidgets('restoring a popped route', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo'); |
| await tester.pumpAndSettle(); |
| |
| await tapRouteCounter('Foo', tester); |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('home'), findsNothing); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 3), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo'), findsNothing); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| }); |
| |
| testWidgets('popped routes are not restored', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Bar'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('Bar'), findsNothing); |
| expect(findRoute('Foo'), findsOneWidget); |
| expect(findRoute('home', skipOffstage: false), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('Bar'), findsNothing); |
| expect(findRoute('Foo'), findsOneWidget); |
| expect(findRoute('home', skipOffstage: false), findsOneWidget); |
| }); |
| |
| testWidgets('routes that are in the process of push are restored', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo'); |
| await tester.pump(); |
| await tester.pump(); |
| expect(findRoute('Foo'), findsOneWidget); |
| |
| // Push is in progress. |
| final ModalRoute<Object> route1 = ModalRoute.of(tester.element(find.text('Route: Foo')))!; |
| final String route1id = route1.restorationScopeId.value!; |
| expect(route1id, isNotNull); |
| expect(route1.settings.name, 'Foo'); |
| expect(route1.animation!.isCompleted, isFalse); |
| expect(route1.animation!.isDismissed, isFalse); |
| expect(route1.isActive, isTrue); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('Foo'), findsOneWidget); |
| expect(findRoute('home', skipOffstage: false), findsOneWidget); |
| final ModalRoute<Object> route2 = ModalRoute.of(tester.element(find.text('Route: Foo')))!; |
| expect(route2, isNot(same(route1))); |
| expect(route1.restorationScopeId.value, route1id); |
| expect(route2.animation!.isCompleted, isTrue); |
| expect(route2.isActive, isTrue); |
| }); |
| |
| testWidgets('routes that are in the process of pop are not restored', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo'); |
| await tester.pumpAndSettle(); |
| |
| final ModalRoute<Object> route1 = ModalRoute.of(tester.element(find.text('Route: Foo')))!; |
| int notifyCount = 0; |
| route1.restorationScopeId.addListener(() { |
| notifyCount++; |
| }); |
| expect(route1.isActive, isTrue); |
| expect(route1.restorationScopeId.value, isNotNull); |
| expect(route1.animation!.isCompleted, isTrue); |
| expect(notifyCount, 0); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| expect(notifyCount, 1); |
| await tester.pump(); |
| await tester.pump(); |
| |
| // Pop is in progress. |
| expect(route1.restorationScopeId.value, isNull); |
| expect(route1.settings.name, 'Foo'); |
| expect(route1.animation!.isCompleted, isFalse); |
| expect(route1.animation!.isDismissed, isFalse); |
| expect(route1.isActive, isFalse); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('Foo', skipOffstage: false), findsNothing); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| expect(notifyCount, 1); |
| }); |
| |
| testWidgets('routes are restored in the right order', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route1'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route1'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route2'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route2'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route3'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route3'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route4'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route4'), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('route4'), findsOneWidget); |
| expect(findRoute('route3', skipOffstage: false), findsOneWidget); |
| expect(findRoute('route2', skipOffstage: false), findsOneWidget); |
| expect(findRoute('route1', skipOffstage: false), findsOneWidget); |
| expect(findRoute('home', skipOffstage: false), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route3'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route2'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route1'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('home'), findsOneWidget); |
| }); |
| |
| testWidgets('all routes up to first unrestorable are restored', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route1'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route1'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route2'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route2'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('route3'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route3'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route4'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route4'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePush(_routeBuilder, arguments: 'route5'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route5'), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('route5', skipOffstage: false), findsNothing); |
| expect(findRoute('route4', skipOffstage: false), findsNothing); |
| expect(findRoute('route3', skipOffstage: false), findsNothing); |
| |
| expect(findRoute('route2'), findsOneWidget); |
| expect(findRoute('route1', skipOffstage: false), findsOneWidget); |
| expect(findRoute('home', skipOffstage: false), findsOneWidget); |
| }); |
| |
| testWidgets('removing unrestorable routes restores all of them', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route1'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route1'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route2'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route2'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('route3'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route3'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route4'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route4'), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('route5'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('route5'), findsOneWidget); |
| |
| final Route<Object> route = ModalRoute.of(tester.element(find.text('Route: route3', skipOffstage: false)))!; |
| expect(route.settings.name, 'route3'); |
| tester.state<NavigatorState>(find.byType(Navigator)).removeRoute(route); |
| await tester.pumpAndSettle(); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('route5'), findsOneWidget); |
| expect(findRoute('route4', skipOffstage: false), findsOneWidget); |
| expect(findRoute('route3', skipOffstage: false), findsNothing); |
| expect(findRoute('route2', skipOffstage: false), findsOneWidget); |
| expect(findRoute('route1', skipOffstage: false), findsOneWidget); |
| expect(findRoute('home', skipOffstage: false), findsOneWidget); |
| }); |
| |
| testWidgets('RestorableRouteFuture', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePush(_routeFutureBuilder); |
| await tester.pumpAndSettle(); |
| expect(find.text('Return value: null'), findsOneWidget); |
| |
| final RestorableRouteFuture<int> routeFuture = tester |
| .state<RouteFutureWidgetState>(find.byType(RouteFutureWidget)) |
| .routeFuture; |
| expect(routeFuture.route, isNull); |
| expect(routeFuture.isPresent, isFalse); |
| expect(routeFuture.enabled, isFalse); |
| |
| routeFuture.present('Foo'); |
| await tester.pumpAndSettle(); |
| expect(find.text('Route: Foo'), findsOneWidget); |
| expect(routeFuture.route!.settings.name, 'Foo'); |
| expect(routeFuture.isPresent, isTrue); |
| expect(routeFuture.enabled, isTrue); |
| |
| await tester.restartAndRestore(); |
| |
| expect(find.text('Route: Foo'), findsOneWidget); |
| final RestorableRouteFuture<int> restoredRouteFuture = tester |
| .state<RouteFutureWidgetState>(find.byType(RouteFutureWidget, skipOffstage: false)) |
| .routeFuture; |
| expect(restoredRouteFuture.route!.settings.name, 'Foo'); |
| expect(restoredRouteFuture.isPresent, isTrue); |
| expect(restoredRouteFuture.enabled, isTrue); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(10); |
| await tester.pumpAndSettle(); |
| expect(find.text('Return value: 10'), findsOneWidget); |
| expect(restoredRouteFuture.route, isNull); |
| expect(restoredRouteFuture.isPresent, isFalse); |
| expect(restoredRouteFuture.enabled, isFalse); |
| }, skip: isBrowser); // https://github.com/flutter/flutter/issues/33615 |
| |
| testWidgets('RestorableRouteFuture in unrestorable context', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| expect(findRoute('home'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('unrestorable'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('unrestorable'), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePush(_routeFutureBuilder); |
| await tester.pumpAndSettle(); |
| expect(find.text('Return value: null'), findsOneWidget); |
| |
| final RestorableRouteFuture<int> routeFuture = tester |
| .state<RouteFutureWidgetState>(find.byType(RouteFutureWidget)) |
| .routeFuture; |
| expect(routeFuture.route, isNull); |
| expect(routeFuture.isPresent, isFalse); |
| expect(routeFuture.enabled, isFalse); |
| |
| routeFuture.present('Foo'); |
| await tester.pumpAndSettle(); |
| expect(find.text('Route: Foo'), findsOneWidget); |
| expect(routeFuture.route!.settings.name, 'Foo'); |
| expect(routeFuture.isPresent, isTrue); |
| expect(routeFuture.enabled, isFalse); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home'), findsOneWidget); |
| }); |
| |
| testWidgets('Illegal arguments throw', (WidgetTester tester) async { |
| await tester.pumpWidget(const TestWidget()); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Bar'); |
| await tester.pumpAndSettle(); |
| |
| final Route<Object> oldRoute = ModalRoute.of(tester.element(find.text('Route: Bar')))!; |
| expect(oldRoute.settings.name, 'Bar'); |
| |
| final Matcher throwsArgumentsAssertionError = throwsA(isAssertionError.having( |
| (AssertionError e) => e.message, |
| 'message', |
| 'The arguments object must be serializable via the StandardMessageCodec.', |
| )); |
| final Matcher throwsBuilderAssertionError = throwsA(isAssertionError.having( |
| (AssertionError e) => e.message, |
| 'message', |
| 'The provided routeBuilder must be a static function.', |
| )); |
| |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo', arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePushReplacementNamed('Foo', arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePopAndPushNamed('Foo', arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamedAndRemoveUntil('Foo', (Route<Object?> _) => false, arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePush(_routeBuilder, arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePushReplacement(_routeBuilder, arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePushAndRemoveUntil(_routeBuilder, (Route<Object?> _) => false, arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorableReplace(newRouteBuilder: _routeBuilder, oldRoute: oldRoute, arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorableReplaceRouteBelow(newRouteBuilder: _routeBuilder, anchorRoute: oldRoute, arguments: Object()), |
| throwsArgumentsAssertionError, |
| ); |
| |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePush((BuildContext _, Object? __) => FakeRoute()), |
| throwsBuilderAssertionError, |
| skip: isBrowser, // https://github.com/flutter/flutter/issues/33615 |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePushReplacement((BuildContext _, Object? __) => FakeRoute()), |
| throwsBuilderAssertionError, |
| skip: isBrowser, // https://github.com/flutter/flutter/issues/33615 |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorablePushAndRemoveUntil((BuildContext _, Object? __) => FakeRoute(), (Route<Object?> _) => false), |
| throwsBuilderAssertionError, |
| skip: isBrowser, // https://github.com/flutter/flutter/issues/33615 |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorableReplace(newRouteBuilder: (BuildContext _, Object? __) => FakeRoute(), oldRoute: oldRoute), |
| throwsBuilderAssertionError, |
| skip: isBrowser, // https://github.com/flutter/flutter/issues/33615 |
| ); |
| expect( |
| () => tester.state<NavigatorState>(find.byType(Navigator)).restorableReplaceRouteBelow(newRouteBuilder: (BuildContext _, Object? __) => FakeRoute(), anchorRoute: oldRoute), |
| throwsBuilderAssertionError, |
| skip: isBrowser, // https://github.com/flutter/flutter/issues/33615 |
| ); |
| }); |
| |
| testWidgets('Moving scopes', (WidgetTester tester) async { |
| await tester.pumpWidget(const RootRestorationScope( |
| restorationId: 'root', |
| child: TestWidget( |
| restorationId: null, |
| ), |
| )); |
| await tapRouteCounter('home', tester); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo'), findsOneWidget); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| |
| // Nothing is restored. |
| await tester.restartAndRestore(); |
| expect(findRoute('Foo'), findsNothing); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| |
| await tapRouteCounter('home', tester); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo'); |
| await tester.pumpAndSettle(); |
| |
| // Move navigator into restoration scope. |
| await tester.pumpWidget(const RootRestorationScope( |
| restorationId: 'root', |
| child: TestWidget(), |
| )); |
| |
| expect(findRoute('Foo'), findsOneWidget); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| |
| // Everything is restored. |
| await tester.restartAndRestore(); |
| expect(findRoute('Foo'), findsOneWidget); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| |
| // Move navigator out of restoration scope. |
| await tester.pumpWidget(const RootRestorationScope( |
| restorationId: 'root', |
| child: TestWidget( |
| restorationId: null, |
| ), |
| )); |
| |
| expect(findRoute('Foo'), findsOneWidget); |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| |
| // Nothing is restored. |
| await tester.restartAndRestore(); |
| expect(findRoute('Foo'), findsNothing); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| }); |
| |
| testWidgets('Restoring pages', (WidgetTester tester) async { |
| await tester.pumpWidget(const PagedTestWidget()); |
| expect(findRoute('home', count: 0), findsOneWidget); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('Foo', tester); |
| await tapRouteCounter('Foo', tester); |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| |
| final TestRestorationData data = await tester.getRestorationData(); |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).addPage('bar'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('bar', tester); |
| expect(findRoute('bar', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('Foo'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('Foo', count: 0), findsOneWidget); |
| |
| await tester.restoreFrom(data); |
| |
| expect(findRoute('bar', skipOffstage: false), findsNothing); |
| expect(findRoute('Foo', count: 2), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).addPage('bar'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('bar', count: 0), findsOneWidget); |
| }); |
| |
| testWidgets('Unrestorable pages', (WidgetTester tester) async { |
| await tester.pumpWidget(const PagedTestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).addPage('p1'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('p1', tester); |
| expect(findRoute('p1', count: 1), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('r1'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('r1', tester); |
| expect(findRoute('r1', count: 1), findsOneWidget); |
| |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).addPage('p2', restoreState: false); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('p2', tester); |
| expect(findRoute('p2', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('r2'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('r2', tester); |
| expect(findRoute('r2', count: 1), findsOneWidget); |
| |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).addPage('p3'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('p3', tester); |
| expect(findRoute('p3', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('r3'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('r3', tester); |
| expect(findRoute('r3', count: 1), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('r2', skipOffstage: false), findsNothing); |
| expect(findRoute('r3', count: 1), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('p3', count: 1), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('p2', count: 0), findsOneWidget); // Page did not restore its state! |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('r1', count: 1), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('p1', count: 1), findsOneWidget); |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pumpAndSettle(); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| }); |
| |
| testWidgets('removed page is not restored', (WidgetTester tester) async { |
| await tester.pumpWidget(const PagedTestWidget()); |
| await tapRouteCounter('home', tester); |
| expect(findRoute('home', count: 1), findsOneWidget); |
| |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).addPage('p1'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('p1', tester); |
| expect(findRoute('p1', count: 1), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).restorablePushNamed('r1'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('r1', tester); |
| expect(findRoute('r1', count: 1), findsOneWidget); |
| |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).addPage('p2'); |
| await tester.pumpAndSettle(); |
| await tapRouteCounter('p2', tester); |
| expect(findRoute('p2', count: 1), findsOneWidget); |
| |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).removePage('p1'); |
| await tester.pumpAndSettle(); |
| |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('p1', count: 1, skipOffstage: false), findsNothing); |
| expect(findRoute('r1', count: 1, skipOffstage: false), findsNothing); |
| expect(findRoute('p2', count: 1), findsOneWidget); |
| |
| await tester.restartAndRestore(); |
| |
| expect(findRoute('home', count: 1, skipOffstage: false), findsOneWidget); |
| expect(findRoute('p1', count: 1, skipOffstage: false), findsNothing); |
| expect(findRoute('r1', count: 1, skipOffstage: false), findsNothing); |
| expect(findRoute('p2', count: 1), findsOneWidget); |
| |
| tester.state<PagedTestNavigatorState>(find.byType(PagedTestNavigator)).addPage('p1'); |
| await tester.pumpAndSettle(); |
| expect(findRoute('p1', count: 0), findsOneWidget); |
| }); |
| |
| testWidgets('Helpful assert thrown all routes in onGenerateInitialRoutes are not restorable', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| restorationScopeId: 'material_app', |
| initialRoute: '/', |
| routes: <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Container(), |
| }, |
| onGenerateInitialRoutes: (String initialRoute) { |
| return <MaterialPageRoute<void>>[ |
| MaterialPageRoute<void>( |
| builder: (BuildContext context) => Container(), |
| ), |
| ]; |
| }, |
| ), |
| ); |
| await tester.restartAndRestore(); |
| final dynamic exception = tester.takeException(); |
| expect(exception, isAssertionError); |
| expect( |
| (exception as AssertionError).message, |
| contains('All routes returned by onGenerateInitialRoutes are not restorable.'), |
| ); |
| |
| // The previous assert leaves the widget tree in a broken state, so the |
| // following code catches any remaining exceptions from attempting to build |
| // new widget tree. |
| final FlutterExceptionHandler? oldHandler = FlutterError.onError; |
| dynamic remainingException; |
| FlutterError.onError = (FlutterErrorDetails details) { |
| remainingException ??= details.exception; |
| }; |
| await tester.pumpWidget(Container(key: UniqueKey())); |
| FlutterError.onError = oldHandler; |
| expect(remainingException, isAssertionError); |
| }); |
| } |
| |
| Route<void> _routeBuilder(BuildContext context, Object? arguments) { |
| return MaterialPageRoute<void>( |
| builder: (BuildContext context) { |
| return RouteWidget( |
| name: arguments! as String, |
| ); |
| }, |
| ); |
| } |
| |
| Route<void> _routeFutureBuilder(BuildContext context, Object? arguments) { |
| return MaterialPageRoute<void>( |
| builder: (BuildContext context) { |
| return const RouteFutureWidget(); |
| }, |
| ); |
| } |
| |
| class PagedTestWidget extends StatelessWidget { |
| const PagedTestWidget({Key? key, this.restorationId = 'app'}) : super(key: key); |
| |
| final String restorationId; |
| |
| @override |
| Widget build(BuildContext context) { |
| return RootRestorationScope( |
| restorationId: restorationId, |
| child: const Directionality( |
| textDirection: TextDirection.ltr, |
| child: PagedTestNavigator(), |
| ), |
| ); |
| } |
| } |
| |
| class PagedTestNavigator extends StatefulWidget { |
| const PagedTestNavigator({Key? key}) : super(key: key); |
| |
| @override |
| State<PagedTestNavigator> createState() => PagedTestNavigatorState(); |
| } |
| |
| class PagedTestNavigatorState extends State<PagedTestNavigator> with RestorationMixin { |
| final RestorableString _routes = RestorableString('r-home'); |
| |
| void addPage(String name, {bool restoreState = true, int? index}) { |
| assert(!name.contains(',')); |
| assert(!name.startsWith('r-')); |
| final List<String> routes = _routes.value.split(','); |
| name = restoreState ? 'r-$name' : name; |
| if (index != null) { |
| routes.insert(index, name); |
| } else { |
| routes.add(name); |
| } |
| setState(() { |
| _routes.value = routes.join(','); |
| }); |
| } |
| |
| bool removePage(String name) { |
| final List<String> routes = _routes.value.split(','); |
| if (routes.remove(name) || routes.remove('r-$name')) { |
| setState(() { |
| _routes.value = routes.join(','); |
| }); |
| return true; |
| } |
| return false; |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return Navigator( |
| restorationScopeId: 'nav', |
| onPopPage: (Route<dynamic> route, dynamic result) { |
| if (route.didPop(result)) { |
| removePage(route.settings.name!); |
| return true; |
| } |
| return false; |
| }, |
| pages: _routes.value.isEmpty ? const <Page<Object?>>[] : _routes.value.split(',').map((String name) { |
| if (name.startsWith('r-')) { |
| name = name.substring(2); |
| return TestPage( |
| name: name, |
| restorationId: name, |
| key: ValueKey<String>(name), |
| ); |
| } |
| return TestPage( |
| name: name, |
| key: ValueKey<String>(name), |
| ); |
| }).toList(), |
| onGenerateRoute: (RouteSettings settings) { |
| return MaterialPageRoute<int>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return RouteWidget( |
| name: settings.name!, |
| arguments: settings.arguments, |
| ); |
| }, |
| ); |
| }, |
| ); |
| } |
| |
| @override |
| String get restorationId => 'router'; |
| |
| @override |
| void restoreState(RestorationBucket? oldBucket, bool initialRestore) { |
| registerForRestoration(_routes, 'routes'); |
| } |
| |
| @override |
| void dispose() { |
| super.dispose(); |
| _routes.dispose(); |
| } |
| } |
| |
| class TestPage extends Page<void> { |
| const TestPage({LocalKey? key, required String name, String? restorationId}) : super(name: name, key: key, restorationId: restorationId); |
| |
| @override |
| Route<void> createRoute(BuildContext context) { |
| return MaterialPageRoute<void>( |
| settings: this, |
| builder: (BuildContext context) { |
| return RouteWidget( |
| name: name!, |
| ); |
| }, |
| ); |
| } |
| } |
| |
| class TestWidget extends StatelessWidget { |
| const TestWidget({Key? key, this.restorationId = 'app'}) : super(key: key); |
| |
| final String? restorationId; |
| |
| @override |
| Widget build(BuildContext context) { |
| return RootRestorationScope( |
| restorationId: restorationId, |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| child: Navigator( |
| initialRoute: 'home', |
| restorationScopeId: 'app', |
| onGenerateRoute: (RouteSettings settings) { |
| return MaterialPageRoute<int>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return RouteWidget( |
| name: settings.name!, |
| arguments: settings.arguments, |
| ); |
| }, |
| ); |
| }, |
| ), |
| ), |
| ); |
| } |
| } |
| |
| class RouteWidget extends StatefulWidget { |
| const RouteWidget({Key? key, required this.name, this.arguments}) : super(key: key); |
| |
| final String name; |
| final Object? arguments; |
| |
| @override |
| State<RouteWidget> createState() => RouteWidgetState(); |
| } |
| |
| class RouteWidgetState extends State<RouteWidget> with RestorationMixin { |
| final RestorableInt counter = RestorableInt(0); |
| |
| @override |
| void restoreState(RestorationBucket? oldBucket, bool initialRestore) { |
| registerForRestoration(counter, 'counter'); |
| } |
| |
| @override |
| String get restorationId => 'stateful'; |
| |
| @override |
| void dispose() { |
| super.dispose(); |
| counter.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return Center( |
| child: Column( |
| children: <Widget>[ |
| GestureDetector( |
| child: Text('Route: ${widget.name}'), |
| onTap: () { |
| setState(() { |
| counter.value++; |
| }); |
| }, |
| ), |
| if (widget.arguments != null) |
| Text('Arguments(home): ${widget.arguments}'), |
| Text('Counter(${widget.name}): ${counter.value}'), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class RouteFutureWidget extends StatefulWidget { |
| const RouteFutureWidget({Key? key}): super(key: key); |
| |
| @override |
| State<RouteFutureWidget> createState() => RouteFutureWidgetState(); |
| } |
| |
| class RouteFutureWidgetState extends State<RouteFutureWidget> with RestorationMixin { |
| late RestorableRouteFuture<int> routeFuture; |
| int? value; |
| |
| @override |
| void initState() { |
| super.initState(); |
| routeFuture = RestorableRouteFuture<int>( |
| onPresent: (NavigatorState navigatorState, Object? arguments) { |
| return navigatorState.restorablePushNamed(arguments! as String); |
| }, |
| onComplete: (int i) { |
| setState(() { |
| value = i; |
| }); |
| }, |
| ); |
| } |
| |
| @override |
| void restoreState(RestorationBucket? oldBucket, bool initialRestore) { |
| registerForRestoration(routeFuture, 'routeFuture'); |
| } |
| |
| @override |
| String get restorationId => 'routefuturewidget'; |
| |
| @override |
| void dispose() { |
| super.dispose(); |
| routeFuture.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return Center( |
| child: Text('Return value: $value'), |
| ); |
| } |
| } |
| |
| Finder findRoute(String name, { Object? arguments, int? count, bool skipOffstage = true }) => _RouteFinder(name, arguments: arguments, count: count, skipOffstage: skipOffstage); |
| |
| Future<void> tapRouteCounter(String name, WidgetTester tester) async { |
| await tester.tap(find.text('Route: $name')); |
| await tester.pump(); |
| } |
| |
| class _RouteFinder extends MatchFinder { |
| _RouteFinder(this.name, { this.arguments, this.count, bool skipOffstage = true }) : super(skipOffstage: skipOffstage); |
| |
| final String name; |
| final Object? arguments; |
| final int? count; |
| |
| @override |
| String get description { |
| String result = 'Route(name: $name'; |
| if (arguments != null) { |
| result += ', arguments: $arguments'; |
| } |
| if (count != null) { |
| result += ', count: $count'; |
| } |
| return result; |
| } |
| |
| @override |
| bool matches(Element candidate) { |
| final Widget widget = candidate.widget; |
| if (widget is RouteWidget) { |
| if (widget.name != name) { |
| return false; |
| } |
| if (arguments != null && widget.arguments != arguments) { |
| return false; |
| } |
| final RouteWidgetState state = (candidate as StatefulElement).state as RouteWidgetState; |
| if (count != null && state.counter.value != count) { |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| class FakeRoute extends Fake implements Route<void> { } |