1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// 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('Router state restoration without RouteInformationProvider', (WidgetTester tester) async {
final UniqueKey router = UniqueKey();
_TestRouterDelegate delegate() => tester.widget<Router<Object?>>(find.byKey(router)).routerDelegate as _TestRouterDelegate;
await tester.pumpWidget(_TestWidget(routerKey: router));
expect(find.text('Current config: null'), findsOneWidget);
expect(delegate().newRoutePaths, isEmpty);
expect(delegate().restoredRoutePaths, isEmpty);
delegate().currentConfiguration = '/foo';
await tester.pumpAndSettle();
expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, isEmpty);
expect(delegate().restoredRoutePaths, isEmpty);
await tester.restartAndRestore();
expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, isEmpty);
expect(delegate().restoredRoutePaths, <String>['/foo']);
final TestRestorationData restorationData = await tester.getRestorationData();
delegate().currentConfiguration = '/bar';
await tester.pumpAndSettle();
expect(find.text('Current config: /bar'), findsOneWidget);
expect(delegate().newRoutePaths, isEmpty);
expect(delegate().restoredRoutePaths, <String>['/foo']);
await tester.restoreFrom(restorationData);
expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, isEmpty);
expect(delegate().restoredRoutePaths, <String>['/foo', '/foo']);
});
testWidgets('Router state restoration with RouteInformationProvider', (WidgetTester tester) async {
final UniqueKey router = UniqueKey();
_TestRouterDelegate delegate() => tester.widget<Router<Object?>>(find.byKey(router)).routerDelegate as _TestRouterDelegate;
_TestRouteInformationProvider provider() => tester.widget<Router<Object?>>(find.byKey(router)).routeInformationProvider! as _TestRouteInformationProvider;
await tester.pumpWidget(_TestWidget(routerKey: router, withInformationProvider: true));
expect(find.text('Current config: /home'), findsOneWidget);
expect(delegate().newRoutePaths, <String>['/home']);
expect(delegate().restoredRoutePaths, isEmpty);
provider().value = RouteInformation(uri: Uri(path: '/foo'));
await tester.pumpAndSettle();
expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, <String>['/home', '/foo']);
expect(delegate().restoredRoutePaths, isEmpty);
await tester.restartAndRestore();
expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, isEmpty);
expect(delegate().restoredRoutePaths, <String>['/foo']);
final TestRestorationData restorationData = await tester.getRestorationData();
provider().value = RouteInformation(uri: Uri.parse('/bar'));
await tester.pumpAndSettle();
expect(find.text('Current config: /bar'), findsOneWidget);
expect(delegate().newRoutePaths, <String>['/bar']);
expect(delegate().restoredRoutePaths, <String>['/foo']);
await tester.restoreFrom(restorationData);
expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, <String>['/bar']);
expect(delegate().restoredRoutePaths, <String>['/foo', '/foo']);
});
}
class _TestRouteInformationParser extends RouteInformationParser<String> {
@override
Future<String> parseRouteInformation(RouteInformation routeInformation) {
return SynchronousFuture<String>(routeInformation.uri.toString());
}
@override
RouteInformation? restoreRouteInformation(String configuration) {
return RouteInformation(uri: Uri.parse(configuration));
}
}
class _TestRouterDelegate extends RouterDelegate<String> with ChangeNotifier {
_TestRouterDelegate() {
if (kFlutterMemoryAllocationsEnabled) {
ChangeNotifier.maybeDispatchObjectCreation(this);
}
}
final List<String> newRoutePaths = <String>[];
final List<String> restoredRoutePaths = <String>[];
@override
String? get currentConfiguration => _currentConfiguration;
String? _currentConfiguration;
set currentConfiguration(String? value) {
if (value == _currentConfiguration) {
return;
}
_currentConfiguration = value;
notifyListeners();
}
@override
Future<void> setNewRoutePath(String configuration) {
_currentConfiguration = configuration;
newRoutePaths.add(configuration);
return SynchronousFuture<void>(null);
}
@override
Future<void> setRestoredRoutePath(String configuration) {
_currentConfiguration = configuration;
restoredRoutePaths.add(configuration);
return SynchronousFuture<void>(null);
}
@override
Widget build(BuildContext context) {
return Text('Current config: $currentConfiguration', textDirection: TextDirection.ltr);
}
@override
Future<bool> popRoute() async => throw UnimplementedError();
}
class _TestRouteInformationProvider extends RouteInformationProvider with ChangeNotifier {
_TestRouteInformationProvider() {
if (kFlutterMemoryAllocationsEnabled) {
ChangeNotifier.maybeDispatchObjectCreation(this);
}
}
@override
RouteInformation get value => _value;
RouteInformation _value = RouteInformation(uri: Uri.parse('/home'));
set value(RouteInformation value) {
if (value == _value) {
return;
}
_value = value;
notifyListeners();
}
}
class _TestWidget extends StatefulWidget {
const _TestWidget({this.withInformationProvider = false, this.routerKey});
final bool withInformationProvider;
final Key? routerKey;
@override
State<_TestWidget> createState() => _TestWidgetState();
}
class _TestWidgetState extends State<_TestWidget> {
final _TestRouterDelegate _delegate = _TestRouterDelegate();
final _TestRouteInformationProvider _routeInformationProvider = _TestRouteInformationProvider();
@override
void dispose() {
_delegate.dispose();
_routeInformationProvider.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RootRestorationScope(
restorationId: 'root',
child: Router<String>(
key: widget.routerKey,
restorationScopeId: 'router',
routerDelegate: _delegate,
routeInformationParser: _TestRouteInformationParser(),
routeInformationProvider: widget.withInformationProvider ? _routeInformationProvider : null,
),
);
}
}