From 39be8a40b59dd33aa49a7ebe3fa9d4c41aa6bfcc Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Fri, 21 Aug 2020 10:51:05 -0700 Subject: [PATCH] More restoration documentation (#63438) --- dev/snippets/config/templates/README.md | 5 ++ .../stateful_widget_restoration.tmpl | 46 ++++++++++++ .../src/widgets/restoration_properties.dart | 75 +++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 dev/snippets/config/templates/stateful_widget_restoration.tmpl diff --git a/dev/snippets/config/templates/README.md b/dev/snippets/config/templates/README.md index 6671b517226..6f4495570b2 100644 --- a/dev/snippets/config/templates/README.md +++ b/dev/snippets/config/templates/README.md @@ -87,6 +87,11 @@ the widgets library. `stateful_widget` template, with the addition of the `TickerProviderStateMixin` class, enabling easy generation of animated samples. +- [`stateful_widget_restoration`](stateful_widget_restoration.tmpl) : Similar to + the `stateful_widget` template, but the widget also imports `RestorationMixin` + and has a `restorationId` field which it uses to implement the `restorationId` + getter on the `State`. + - [`stateless_widget`](stateless_widget.tmpl) : Identical to the `stateful_widget` template, except that the default code block is inserted as a method (which should be the `build` method) in a diff --git a/dev/snippets/config/templates/stateful_widget_restoration.tmpl b/dev/snippets/config/templates/stateful_widget_restoration.tmpl new file mode 100644 index 00000000000..1aeb1413cdb --- /dev/null +++ b/dev/snippets/config/templates/stateful_widget_restoration.tmpl @@ -0,0 +1,46 @@ +/// Flutter code sample for {{element}} + +{{description}} + +import 'package:flutter/material.dart'; + +{{code-imports}} + +void main() => runApp(new MyApp()); + +/// This is the main application widget. +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return WidgetsApp( + title: 'Flutter Code Sample', + home: Center( + child: MyStatefulWidget(restorationId: 'main'), + ), + color: const Color(0xffffffff), + ); + } +} + +{{code-preamble}} + +/// This is the stateful widget that the main application instantiates. +class MyStatefulWidget extends StatefulWidget { + MyStatefulWidget({Key key, this.restorationId}) : super(key: key); + + final String restorationId; + + @override + _MyStatefulWidgetState createState() => _MyStatefulWidgetState(); +} + +/// This is the private State class that goes with MyStatefulWidget. +/// RestorationProperty objects can be used because of RestorationMixin. +class _MyStatefulWidgetState extends State with RestorationMixin { + // In this example, the restoration ID for the mixin is passed in through + // the [StatefulWidget]'s constructor. + @override + String get restorationId => widget.restorationId; + +{{code}} +} diff --git a/packages/flutter/lib/src/widgets/restoration_properties.dart b/packages/flutter/lib/src/widgets/restoration_properties.dart index 3ac8e985ac4..c90389ec4dd 100644 --- a/packages/flutter/lib/src/widgets/restoration_properties.dart +++ b/packages/flutter/lib/src/widgets/restoration_properties.dart @@ -19,6 +19,81 @@ import 'restoration.dart'; /// call [notifyListeners] from this method if the new value changes what /// [toPrimitives] returns. /// +/// ## Using a RestorableValue +/// +/// {@tool dartpad --template=stateful_widget_restoration} +/// A [StatefulWidget] that has a restorable [int] property. +/// +/// ```dart +/// // The current value of the answer is stored in a [RestorableProperty]. +/// // During state restoration it is automatically restored to its old value. +/// // If no restoration data is available to restore the answer from, it is +/// // initialized to the specified default value, in this case 42. +/// RestorableInt _answer = RestorableInt(42); +/// +/// @override +/// void restoreState(RestorationBucket oldBucket, bool initialRestore) { +/// // All restorable properties must be registered with the mixin. After +/// // registration, the answer either has its old value restored or is +/// // initialized to its default value. +/// registerForRestoration(_answer, 'answer'); +/// } +/// +/// void _incrementAnswer() { +/// setState(() { +/// // The current value of the property can be accessed and modified via +/// // the value getter and setter. +/// _answer.value += 1; +/// }); +/// } +/// +/// @override +/// void dispose() { +/// // Properties must be disposed when no longer used. +/// _answer.dispose(); +/// super.dispose(); +/// } +/// +/// @override +/// Widget build(BuildContext context) { +/// return OutlinedButton( +/// child: Text('${_answer.value}'), +/// onPressed: _incrementAnswer, +/// ); +/// } +/// ``` +/// {@end-tool} +/// +/// ## Creating a subclass +/// +/// {@tool snippet} +/// This example shows how to create a new `RestorableValue` subclass, +/// in this case for the [Duration] class. +/// +/// ```dart +/// class RestorableDuration extends RestorableValue { +/// @override +/// Duration createDefaultValue() => const Duration(); +/// +/// @override +/// void didUpdateValue(Duration oldValue) { +/// if (oldValue.inMicroseconds != value.inMicroseconds) +/// notifyListeners(); +/// } +/// +/// @override +/// Duration fromPrimitives(Object data) { +/// return Duration(microseconds: data as int); +/// } +/// +/// @override +/// Object toPrimitives() { +/// return value.inMicroseconds; +/// } +/// } +/// ``` +/// {@end-tool} +/// /// See also: /// /// * [RestorableProperty], which is the super class of this class.