blob: 54628343598991f6b0807a5bc5cac1fdbc306616 [file] [log] [blame]
// 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> { }