Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/_data/sidenav.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@
permalink: /development/platform-integration/android/androidx-migration
- title: Deprecated Splash Screen API Migration
permalink: /development/platform-integration/android/splash-screen-migration
- title: Restore state on Android **NEW**
permalink: /development/platform-integration/ios/restore-state-android
- title: Targeting ChromeOS with Android
permalink: /development/platform-integration/android/chromeos
- title: iOS
Expand All @@ -206,6 +208,8 @@
permalink: /development/platform-integration/ios/platform-views
- title: iOS debugging
permalink: /development/platform-integration/ios/ios-debugging
- title: Restore state on iOS **NEW**
permalink: /development/platform-integration/ios/restore-state-ios
- title: Linux
permalink: /development/platform-integration/linux
children:
Expand Down Expand Up @@ -348,7 +352,7 @@
children:
- title: Debugging tools
permalink: /testing/debugging
- title: Testing plugins **NEW**
- title: Testing plugins
permalink: /testing/testing-plugins
- title: Debugging apps programmatically
permalink: /testing/code-debugging
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
title: "State restoration for Android apps"
description: "How to restore the state of your Android app after it's been killed by the OS."
---

When a user runs a mobile app and then selects another
app to run, the first app is moved to the background,
or _backgrounded_. The operating system (both iOS and Android)
typically kill the backgrounded app to release memory and
improve performance for the app running in the foreground.

When the user selects the app again, bringing it
back to the foreground, the OS relaunches it.
But, unless you've set up a way to save the
state of the app before it was killed,
you've lost the state and the app starts from scratch.
The user has lost the continuity they expect,
which is clearly not ideal.
(Imagine filling out a lengthy form and being interrupted
by a phone call _before_ clicking **Submit**.)

So, how can you restore the state of the app so that
it looks like it did before it was sent to the
background?

Flutter has a solution for this with the
[`RestorationManager`][] (and related classes)
in the [services][] library.
With the `RestorationManager`, the Flutter framework
provides the state data to the engine _as the state
changes_, so that the app is ready when the OS signals
that it's about to kill the app, giving the app only
moments to prepare.

{{site.alert.secondary}}
**Instance state vs long-lived state**
When should you use the `RestorationManager` and
when should you save state to long term storage?
_Instance state_
(also called _short-term_ or _ephemeral_ state),
includes unsubmitted form field values, the currently
selected tab, and so on. On Android, this is
limited to 1 MB and, if the app exceeds this,
it crashes with a `TransactionTooLargeException`
error in the native code.
{{site.alert.end}}

[state]: {{site.url}}/development/data-and-backend/state-mgmt/ephemeral-vs-app

## Overview

You can enable state restoration with just a couple tasks:

1. Define a `restorationId` or a `restorationScopeId`
for all widgets that support it,
such as [`TextField`][] and [`ScrollView`][].
This automatically enables built-in state restoration
for those widgets.

2. For custom widgets,
you must decide what state you want to restore
and hold that state in a [`RestorableProperty`][].
(The Flutter API provides various subclasses for
different data types.)
Define those `RestorableProperty` widgets
in a `State` class that uses the [`RestorationMixin`][].
Register those widgets with the mixin in a
`restoreState` method.

Other considerations:

* Providing a `restorationId` to
`MaterialApp`, `CupertinoApp`, or `WidgetsApp`
automatically enables state restoration by
injecting a `RootRestorationScope`.
If you need to restore state _above_ the app class,
inject a `RootRestorationScope` manually.

* **The difference between a `restorationId` and
a `restorationScopeId`:** Widgets that take a
`restorationScopeID` create a new `restorationScope`
(a new `RestorationBucket`) into which all children
store their state. A `restorationId` means the widget
(and its children) store the data in the surrounding bucket.
(QUESTION: Axe this?)

* You can only restore state with the `RestorationMixin`
that is stored in `RestorableProperty` (and its subclasses),
for example, [`restorablePush`][].
To restore the state in those properties,
you have to register them in `restoreState`.
(QUESTION: Axe this?)

* Flutter automatically creates a
[`RestorationScope`][] for each route in your
app to track the `RestorationBucket`s in that scope.
In general, you shouldn't need to worry about
this object _unless_ the scope has two (or more) buckets with
the same `restorationId`. If that occurs,
create a new `RestorationScope` to ensure
that each ID is unique to its scope.
(QUESTION: Axe this?)

[a bit of extra setup]: {{site.api}}/flutter/services/RestorationManager-class.html#state-restoration-on-ios
[`registerForRestoration`]: {{site.api}}/flutter/widgets/RestorationMixin/registerForRestoration.html
[`restorationId`]: {{site.api}}/flutter/widgets/RestorationScope/restorationId.html
[`restorationScopeId`]: {{site.api}}/flutter/widgets/RestorationScope/restorationScopeId.html
[`RestorationMixin`]: {{site.api}}/flutter/widgets/RestorationMixin-mixin.html
[`RestorationScope`]: {{site.api}}/flutter/widgets/RestorationScope-class.html
[`restoreState`]: {{site.api}}/flutter/widgets/RestorationMixin/restoreState.html
[VeggieSeasons]: {{site.github}}/flutter/samples/tree/master/veggieseasons

## Restoring navigation state

If you want your app to return to a particular route
(the shopping cart, for example) that the user was most
recently viewing, then you must implement
restoration state for navigation, as well.

If you use the navigator API directly,
you must use the methods with `restorable`
in the name to ensure that your routes are restored,
such as [`restorablePush`][].

The VeggieSeasons example (listed under "Other resources" below)
implements navigation with the [`go_router`][] package.
Setting the `restorationId`
values occur in the `lib/screens` classes.

## Testing state restoration

To test state restoration, set up your mobile device so that
it doesn't save state once an app is backgrounded.
To learn how to do this for both iOS and Android,
check out [Testing state restoration][] on the
[`RestorationManager`][] page.

{{site.alert.warning}}
**Don't forget to reenable
storing state on your device once you are
finished with testing!**
{{site.alert.end}}

[Testing state restoration]: {{site.api}}/flutter/services/RestorationManager-class.html#testing-state-restoration
[`FlutterViewController`]: {{site.api}}/objcdoc/Classes/FlutterViewController.html
[`RestorationBucket`]: {{site.api}}/flutter/services/RestorationBucket-class.html
[`RestorationManager`]: {{site.api}}/flutter/services/RestorationManager-class.html
[services]: {{site.api}}/flutter/services/services-library.html

## Other resources

For further information on state restoration,
check out the following resources:

* For an example that implements state restoration,
check out [VeggieSeasons][], a sample app written
for iOS that uses Cupertino widgets. An iOS app requires
[a bit of extra setup][] in Xcode, but the restoration
classes otherwise work the same on both iOS and Android.<br>
The following list links to relevant parts of the VeggieSeasons
example:
* [Defining a `RestorablePropery` as an instance property]({{site.github}}/flutter/samples/blob/604c82cd7c9c7807ff6c5ca96fbb01d44a4f2c41/veggieseasons/lib/widgets/trivia.dart#L33-L37)
* [Registering the properties]({{site.github}}/flutter/samples/blob/604c82cd7c9c7807ff6c5ca96fbb01d44a4f2c41/veggieseasons/lib/widgets/trivia.dart#L49-L54)
* [Updating the properties values]({{site.github}}/flutter/samples/blob/604c82cd7c9c7807ff6c5ca96fbb01d44a4f2c41/veggieseasons/lib/widgets/trivia.dart#L108-L109)
* [Using property values in build]({{site.github}}/flutter/samples/blob/604c82cd7c9c7807ff6c5ca96fbb01d44a4f2c41/veggieseasons/lib/widgets/trivia.dart#L205-L210)<br>

* To learn more about short term and long term state,
check out [Differentiate between ephemeral state
and app state][state].

* You might want to check out packages on pub.dev that
perform state restoration, such as [`statePersistence`][].

* For more information on navigation and the
`go_router` package, check out [Navigation and routing][].

[`RestorableProperty`]: {{site.api}}/flutter/widgets/RestorableProperty-class.html
[`RestorationMixin`]: {{site.api}}/flutter/widgets/RestorationMixin-mixin.html
[`restorablePush`]: {{site.api}}/flutter/widgets/Navigator/restorablePush.html
[`ScrollView`]: {{site.api}}/flutter/widgets/ScrollView/restorationId.html
[`statePersistence`]: {{site.pub-pkg}}/state_persistence
[`TextField`]: {{site.api}}/flutter/material/TextField/restorationId.html
[`restorablePush`]: {{site.api}}/flutter/widgets/Navigator/restorablePush.html
[`go_router`]: {{site.pub}}/packages/go_router
[Navigation and routing]: {{site.url}}/development/ui/navigation
25 changes: 25 additions & 0 deletions src/development/platform-integration/ios/restore-state-ios.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: "State restoration on iOS"
description: "How to restore the state of your iOS app after it's been killed by the OS."
---

When a user runs a mobile app and then selects another
app to run, the first app is moved to the background,
or _backgrounded_. The operating system (both iOS and Android)
often kill the backgrounded app to release memory or
improve performance for the app running in the foreground.

You can use the [`RestorationManager`][] (and related)
classes to handle state restoration.
An iOS app requires [a bit of extra setup][] in Xcode,
but the restoration classes otherwise work the same on
both iOS and Android.

For more information, check out [State restoration on Android][]
and the [VeggieSeasons][] code sample.

[a bit of extra setup]: {{site.api}}/flutter/services/RestorationManager-class.html#state-restoration-on-ios
[`RestorationManager`]: {{site.api}}/flutter/services/RestorationManager-class.html
[State restoration on Android]: {{site.url}}/development/platform-integration/android/restore-state-android
[VeggieSeasons]: {{site.github}}/flutter/samples/tree/master/veggieseasons