diff --git a/packages/flutter/lib/src/widgets/page_storage.dart b/packages/flutter/lib/src/widgets/page_storage.dart index c242059d23..243d5799b2 100644 --- a/packages/flutter/lib/src/widgets/page_storage.dart +++ b/packages/flutter/lib/src/widgets/page_storage.dart @@ -6,33 +6,17 @@ import 'package:flutter/foundation.dart'; import 'framework.dart'; -/// A [ValueKey] that defines where [PageStorage] values will be saved. +/// A key can be used to persist the widget state in storage after +/// the destruction and will be restored when recreated. /// -/// [Scrollable]s ([ScrollPosition]s really) use [PageStorage] to save their -/// scroll offset. Each time a scroll completes, the scrollable's page -/// storage is updated. +/// Each key with its value plus the ancestor chain of other PageStorageKeys need to +/// be unique within the widget's closest ancestor [PageStorage]. To make it possible for a +/// saved value to be found when a widget is recreated, the key's value must +/// not be objects whose identity will change each time the widget is created. /// -/// [PageStorage] is used to save and restore values that can outlive the widget. -/// The values are stored in a per-route [Map] whose keys are defined by the -/// [PageStorageKey]s for the widget and its ancestors. To make it possible -/// for a saved value to be found when a widget is recreated, the key's values -/// must not be objects whose identity will change each time the widget is created. +/// See also: /// -/// For example, to ensure that the scroll offsets for the scrollable within -/// each `MyScrollableTabView` below are restored when the [TabBarView] -/// is recreated, we've specified [PageStorageKey]s whose values are the -/// tabs' string labels. -/// -/// ```dart -/// TabBarView( -/// children: myTabs.map((Tab tab) { -/// MyScrollableTabView( -/// key: PageStorageKey(tab.text), // like 'Tab 1' -/// tab: tab, -/// ), -/// }), -/// ) -/// ``` +/// * [PageStorage], which is the closet ancestor for [PageStorageKey]. class PageStorageKey extends ValueKey { /// Creates a [ValueKey] that defines where [PageStorage] values will be saved. const PageStorageKey(T value) : super(value); @@ -130,7 +114,124 @@ class PageStorageBucket { } } -/// A widget that establishes a page storage bucket for this widget subtree. +/// Establish a subtree in which widgets can opt into persisting states after +/// being destroyed. +/// +/// [PageStorage] is used to save and restore values that can outlive the widget. +/// For example, when multiple pages are grouped in tabs, when a page is +/// switched out, its widget is destroyed and its state is lost. By adding a +/// [PageStorage] at the root and adding a [PageStorageKey] to each page, some of the +/// page's state (e.g. the scroll position of a [Scrollable] widget) will be stored +/// automatically in its closest ancestor [PageStorage], and restored when it's +/// switched back. +/// +/// Usually you don't need to explicitly use a [PageStorage], since it's already +/// included in routes. +/// +/// [PageStorageKey] is used by [Scrollable] if +/// `keepScrollOffset` is enabled to save their [ScrollPosition]s. +/// +/// {@tool dartpad --template=freeform} +/// +/// This sample shows how to explicitly use a [PageStorage] to +/// store the states of its children pages. Each page includes a scrollable +/// list, whose position is preserved when switching between the tabs thanks to +/// the help of [PageStorageKey]. +/// +/// ```dart imports +/// import 'package:flutter/material.dart'; +/// ``` +/// +/// ```dart main +/// void main() => runApp(MyApp()); +/// ``` +/// +/// ```dart +/// class MyApp extends StatelessWidget { +/// @override +/// Widget build(BuildContext context) { +/// return MaterialApp( +/// home: MyHomePage(), +/// ); +/// } +/// } +/// +/// class MyHomePage extends StatefulWidget { +/// @override +/// _MyHomePageState createState() => _MyHomePageState(); +/// } +/// +/// class _MyHomePageState extends State { +/// final List pages = [ +/// ColorBoxPage( +/// key: PageStorageKey('pageOne'), +/// ), +/// ColorBoxPage( +/// key: PageStorageKey('pageTwo'), +/// ) +/// ]; +/// int currentTab = 0; +/// final PageStorageBucket _bucket = PageStorageBucket(); +/// +/// @override +/// Widget build(BuildContext context) { +/// return Scaffold( +/// appBar: AppBar( +/// title: Text("Persistance Example"), +/// ), +/// body: PageStorage( +/// child: pages[currentTab], +/// bucket: _bucket, +/// ), +/// bottomNavigationBar: BottomNavigationBar( +/// currentIndex: currentTab, +/// onTap: (int index) { +/// setState(() { +/// currentTab = index; +/// }); +/// }, +/// items: [ +/// BottomNavigationBarItem( +/// icon: Icon(Icons.home), +/// title: Text('page 1'), +/// ), +/// BottomNavigationBarItem( +/// icon: Icon(Icons.settings), +/// title: Text('page2'), +/// ), +/// ], +/// ), +/// ); +/// } +/// } +/// +/// class ColorBoxPage extends StatelessWidget { +/// ColorBoxPage({ +/// Key key, +/// }) : super(key: key); +/// +/// @override +/// Widget build(BuildContext context) { +/// return ListView.builder( +/// itemExtent: 250.0, +/// itemBuilder: (context, index) => Container( +/// padding: EdgeInsets.all(10.0), +/// child: Material( +/// color: index % 2 == 0 ? Colors.cyan : Colors.deepOrange, +/// child: Center( +/// child: Text(index.toString()), +/// ), +/// ), +/// ), +/// ); +/// } +/// } +/// ``` +/// {@end-tool} +/// +/// See also: +/// +/// * [ModalRoute], which includes this class. class PageStorage extends StatelessWidget { /// Creates a widget that provides a storage bucket for its descendants. ///