Make CupertinoTabView restorable (#67169)

This commit is contained in:
Michael Goderbauer 2020-10-06 11:03:13 -07:00 committed by GitHub
parent 4818537044
commit 582c67a125
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 3 deletions

View file

@ -9,9 +9,9 @@ import 'route.dart';
/// A single tab view with its own [Navigator] state and history.
///
/// A typical tab view used as the content of each tab in a [CupertinoTabScaffold]
/// where multiple tabs with parallel navigation states and history can
/// co-exist.
/// A typical tab view is used as the content of each tab in a
/// [CupertinoTabScaffold] where multiple tabs with parallel navigation states
/// and history can co-exist.
///
/// [CupertinoTabView] configures the top-level [Navigator] to search for routes
/// in the following order:
@ -49,6 +49,7 @@ class CupertinoTabView extends StatefulWidget {
this.onGenerateRoute,
this.onUnknownRoute,
this.navigatorObservers = const <NavigatorObserver>[],
this.restorationScopeId,
}) : assert(navigatorObservers != null),
super(key: key);
@ -125,6 +126,12 @@ class CupertinoTabView extends StatefulWidget {
/// This list of observers is not shared with ancestor or descendant [Navigator]s.
final List<NavigatorObserver> navigatorObservers;
/// Restoration ID to save and restore the state of the [Navigator] built by
/// this [CupertinoTabView].
///
/// {@macro flutter.widgets.navigator.restorationScopeId}
final String? restorationScopeId;
@override
_CupertinoTabViewState createState() {
return _CupertinoTabViewState();
@ -164,6 +171,7 @@ class _CupertinoTabViewState extends State<CupertinoTabView> {
onGenerateRoute: _onGenerateRoute,
onUnknownRoute: _onUnknownRoute,
observers: _navigatorObservers,
restorationScopeId: widget.restorationScopeId,
);
}

View file

@ -1528,6 +1528,7 @@ class Navigator extends StatefulWidget {
/// Restoration ID to save and restore the state of the navigator, including
/// its history.
///
/// {@template flutter.widgets.navigator.restorationScopeId}
/// If a restoration ID is provided, the navigator will persist its internal
/// state (including the route history as well as the restorable state of the
/// routes) and restore it during state restoration.
@ -1538,11 +1539,20 @@ class Navigator extends StatefulWidget {
///
/// The state is persisted in a [RestorationBucket] claimed from
/// the surrounding [RestorationScope] using the provided restoration ID.
/// Within that bucket, the [Navigator] also creates a new [RestorationScope]
/// for its children (the [Route]s).
///
/// See also:
///
/// * [RestorationManager], which explains how state restoration works in
/// Flutter.
/// * [RestorationMixin], which contains a runnable code sample showcasing
/// state restoration in Flutter.
/// * [Navigator], which explains under the heading "state restoration"
/// how and under what conditions the navigator restores its state.
/// * [Navigator.restorablePush], which includes an example showcasing how
/// to push a restorable route unto the navigator.
/// {@endtemplate}
final String? restorationScopeId;
/// The name for the default route of the application.

View file

@ -237,4 +237,55 @@ void main() {
' callback returned null. Such callbacks must never return null.\n'
));
});
testWidgets('Navigator of CupertinoTabView restores state', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
restorationScopeId: 'app',
home: CupertinoTabView(
restorationScopeId: 'tab',
builder: (BuildContext context) => CupertinoButton(
child: const Text('home'),
onPressed: () {
Navigator.of(context).restorablePushNamed('/2');
},
),
routes: <String, WidgetBuilder>{
'/2' : (BuildContext context) => const Text('second route'),
}
),
),
);
expect(find.text('home'), findsOneWidget);
await tester.tap(find.text('home'));
await tester.pumpAndSettle();
expect(find.text('home'), findsNothing);
expect(find.text('second route'), findsOneWidget);
final TestRestorationData data = await tester.getRestorationData();
await tester.restartAndRestore();
expect(find.text('home'), findsNothing);
expect(find.text('second route'), findsOneWidget);
Navigator.of(tester.element(find.text('second route'))).pop();
await tester.pumpAndSettle();
expect(find.text('home'), findsOneWidget);
expect(find.text('second route'), findsNothing);
await tester.restoreFrom(data);
expect(find.text('home'), findsNothing);
expect(find.text('second route'), findsOneWidget);
Navigator.of(tester.element(find.text('second route'))).pop();
await tester.pumpAndSettle();
expect(find.text('home'), findsOneWidget);
expect(find.text('second route'), findsNothing);
});
}