Why do we need to manage state when the app is killed?
Imagine a user is filling out a long form in your mobile app. During this process, they need to refer to another document saved on their phone to complete a specific field. What happens when this user minimizes your app to access the document? You might expect that when they return, the app will resume right where they left off. But what if the operating system, running low on memory, decides to terminate your app in the background?
Suddenly, all of that user's progress is lost. They’ll have to start from scratch. This frustrating experience can drive users away, leading to higher drop-off rates and lower user retention. As an engineer, it's crucial to anticipate and handle such edge cases, ensuring that your app manages state efficiently, even when faced with OS-imposed limitations.
User Expectations and Performance Impact
It’s not just about user satisfaction; performance issues directly affect your app’s retention. Studies show that 53% of mobile users will leave an app or website if it takes longer than three seconds to load. Each additional 100 milliseconds of delay can cause users to abandon your app, and poor state management can add significant delays when users re-open your app and are forced to restart their tasks.
Consider this: Amazon reportedly gained 1% additional revenue for every 100ms improvement in page load speed. That's the kind of difference small optimizations, like preserving state, can make in user retention and business performance.
---
This real-world impact highlights how critical state management is, not just in terms of user experience, but also in driving engagement and revenue. Let’s explore how you can use Flutter’s RestorationMixin to solve this issue in your apps.
What is State Restoration?
State restoration refers to the process of saving and restoring the UI state of an application when it is terminated or removed from memory by the operating system. This is particularly important for mobile apps that deal with multi-step forms, media playback, or navigation flows where users expect their progress to persist across sessions.
Why Is It Important?
Mobile operating systems, like Android and iOS, often kill background apps to free up memory. When users reopen these apps, they expect to pick up where they left off, whether it's resuming a video, continuing to fill out a form, or seeing their last navigation position. Without state restoration, users would be forced to start over, leading to a frustrating experience.
How State Restoration Works in Flutter
Flutter introduced state restoration as part of its framework in version 2.0, making it easier to save and restore the UI state of your apps. By using the `RestorationMixin`, Flutter developers can efficiently restore a widget’s state after the app has been closed.
The basic idea is to store the state of your app in `RestorableProperties`, which the framework can save and restore as needed.
Key Classes and Concepts:
1. `RestorationMixin`: A mixin that adds state restoration capabilities to a widget.
2. `RestorableProperty`: Special properties that store values and are restorable, such as `RestorableInt`, `RestorableDouble`, and more.
3. `RestorationId`: A unique identifier for each widget that requires state restoration.
---
When Do You Need State Restoration?
State restoration is useful in a wide range of scenarios, such as:
- Form-based applications: Preserving user input across form pages, especially in multi-step forms.
- Media applications: Resuming a video or music playback from where the user left off.
- Complex navigation: Saving the user’s navigation history and restoring it after relaunch.
Getting Started with `RestorationMixin`
Let’s take a look at how you can implement state restoration in a simple Flutter application with a counter.
Basic Example: Counter App with State Restoration
Here’s a simple example of a counter app where the counter value is restored after the app is killed and relaunched:
import 'package:flutter/material.dart';
class CounterApp extends StatefulWidget {
@override
CounterAppState createState() => CounterAppState();
}
class _CounterAppState extends State<CounterApp> with RestorationMixin {
// Restorable property for saving and restoring the counter value
final RestorableInt _counter = RestorableInt(0);
@override
String? get restorationId => 'counter_app';
// Method that restores the state when the widget is recreated
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_counter, 'counter');
}
@override
void dispose() {
_counter.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Restorable Counter'),
),
body: Center(
child: Text('Counter: ${_counter.value}'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() {
_counter.value++;
}),
child: Icon(Icons.add),
),
);
}
}
void main() => runApp(MaterialApp(
restorationScopeId: 'root',
home: CounterApp(),
));
Get the complete code here: https://docs.flutter.dev/development/ui/advanced/restoration