State Restoration: Preserving UI State Across App Restarts in Flutter
Jul 25, 2025



Summary
Summary
Summary
Summary
This tutorial covers Flutter’s state restoration API—from enabling restorationScopeId in MaterialApp to using RestorationMixin, built-in RestorableProperty types, and custom properties. Learn best practices, advanced use cases like nested navigation and scroll positions, and how to test and clean up state buckets to ensure seamless UI continuity across app restarts.
This tutorial covers Flutter’s state restoration API—from enabling restorationScopeId in MaterialApp to using RestorationMixin, built-in RestorableProperty types, and custom properties. Learn best practices, advanced use cases like nested navigation and scroll positions, and how to test and clean up state buckets to ensure seamless UI continuity across app restarts.
This tutorial covers Flutter’s state restoration API—from enabling restorationScopeId in MaterialApp to using RestorationMixin, built-in RestorableProperty types, and custom properties. Learn best practices, advanced use cases like nested navigation and scroll positions, and how to test and clean up state buckets to ensure seamless UI continuity across app restarts.
This tutorial covers Flutter’s state restoration API—from enabling restorationScopeId in MaterialApp to using RestorationMixin, built-in RestorableProperty types, and custom properties. Learn best practices, advanced use cases like nested navigation and scroll positions, and how to test and clean up state buckets to ensure seamless UI continuity across app restarts.
Key insights:
Key insights:
Key insights:
Key insights:
Understanding Flutter State Restoration: Explains RestorationMixin, RestorationBucket, and RestorableProperty for state serialization.
Enabling State Restoration: Shows how to set restorationScopeId and register RestorableInt to preserve simple values.
Custom Restorable Properties: Demonstrates extending RestorableProperty to store complex objects safely.
Best Practices for State Restoration: Recommends unique IDs, minimal persisted state, async initialization, and cleanup.
Advanced Use Cases: Covers nested navigation, scroll positions, tabs, and deep link restoration tactics.
Introduction
State restoration in Flutter ensures that your app preserves its UI state across restarts and system-initiated kills. Rather than losing form entries, navigation stacks, or scroll positions, Flutter’s restoration framework lets you rebuild the exact previous state when users return. This tutorial covers core concepts, setup, custom properties, best practices, and advanced use cases.
Understanding Flutter State Restoration
Flutter’s restoration API revolves around the RestorationMixin, RestorationBucket, and RestorableProperty. A RestorationBucket acts as a key-value store for state data. Widgets or objects that mix in RestorationMixin register RestorableProperty instances, each linked to a unique restorationId. During a cold start or process death, Flutter rehydrates these properties from serialized data, ensuring your UI state reappears intact.
Key components:
• RestorationScope – scopes a portion of the widget tree with its own bucket.
• RestorationMixin – implemented by State classes to register restorable values.
• RestorableProperty – base class for built-in properties like RestorableInt, RestorableBool, RestorableTextEditingController.
Enabling State Restoration
Enable restoration in MaterialApp or CupertinoApp by assigning a restorationScopeId. Every widget that participates must have a restorationId.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
restorationScopeId: 'root',
home: CounterPage(),
);
}
}
class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> with RestorationMixin {
final RestorableInt _counter = RestorableInt(0);
@override
String get restorationId => 'counter_page';
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_counter, 'count');
}
void _increment() {
setState(() => _counter.value++);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(child: Text('Count: ${_counter.value}')),
floatingActionButton: FloatingActionButton(
onPressed: _increment, child: Icon(Icons.add)),
);
}
}
This setup ensures that when the app is recreated, the counter restores to its last value.
Custom Restorable Properties
When built-in RestorableProperty types don’t meet your needs, implement a custom property by extending RestorableProperty. For example, store a complex object or color:
class RestorableColor extends RestorableProperty<Color> {
Color _color = Colors.blue;
@override
Color createDefaultValue() => _color;
@override
void fromPrimitives(Object? data) {
final map = data as Map<String, dynamic>;
_color = Color(map['value'] as int);
}
@override
Object toPrimitives() {
return {'value': _color.value};
}
@override
Color get value => _color;
set value(Color newColor) {
if (_color != newColor) {
_color = newColor;
notifyListeners();
}
}
}
Register your custom RestorableColor just like built-ins, and the framework handles serialization.
Best Practices for State Restoration
• Use concise restorationIds – keep them unique within each scope.
• Persist only minimal UI state – avoid large data blobs in buckets.
• Handle asynchronous initialization – restore simple values first, then fetch remote data.
• Clean up obsolete buckets – if you rename or remove IDs, call dispose and remove stored fragments.
• Test restoration by simulating process death (e.g., Android Studio’s “Terminate Application” in DevTools).
Advanced Use Cases
• Nested Navigation: Assign restorationScopeId to Navigator widgets to persist their stacks separately.
• Scroll Position: Use ScrollController with RestorableScrollController to restore list offsets.
• Tab State: Wrap TabController in RestorableTabController for tab index persistence.
• Deep Links: Combine restoration with onGenerateInitialRoutes to rebuild link-based navigation.
Each advanced scenario uses the same pattern: mix in RestorationMixin, register RestorableProperty, and assign proper IDs.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Flutter’s state restoration framework empowers you to deliver seamless user experiences even after app restarts. By enabling restoration at the root level, registering built-in or custom RestorableProperty instances, and following best practices, you ensure critical UI state survives process termination. Leverage these tools to maintain counters, form inputs, navigation stacks, scroll positions, and more across sessions.
Introduction
State restoration in Flutter ensures that your app preserves its UI state across restarts and system-initiated kills. Rather than losing form entries, navigation stacks, or scroll positions, Flutter’s restoration framework lets you rebuild the exact previous state when users return. This tutorial covers core concepts, setup, custom properties, best practices, and advanced use cases.
Understanding Flutter State Restoration
Flutter’s restoration API revolves around the RestorationMixin, RestorationBucket, and RestorableProperty. A RestorationBucket acts as a key-value store for state data. Widgets or objects that mix in RestorationMixin register RestorableProperty instances, each linked to a unique restorationId. During a cold start or process death, Flutter rehydrates these properties from serialized data, ensuring your UI state reappears intact.
Key components:
• RestorationScope – scopes a portion of the widget tree with its own bucket.
• RestorationMixin – implemented by State classes to register restorable values.
• RestorableProperty – base class for built-in properties like RestorableInt, RestorableBool, RestorableTextEditingController.
Enabling State Restoration
Enable restoration in MaterialApp or CupertinoApp by assigning a restorationScopeId. Every widget that participates must have a restorationId.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
restorationScopeId: 'root',
home: CounterPage(),
);
}
}
class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> with RestorationMixin {
final RestorableInt _counter = RestorableInt(0);
@override
String get restorationId => 'counter_page';
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_counter, 'count');
}
void _increment() {
setState(() => _counter.value++);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(child: Text('Count: ${_counter.value}')),
floatingActionButton: FloatingActionButton(
onPressed: _increment, child: Icon(Icons.add)),
);
}
}
This setup ensures that when the app is recreated, the counter restores to its last value.
Custom Restorable Properties
When built-in RestorableProperty types don’t meet your needs, implement a custom property by extending RestorableProperty. For example, store a complex object or color:
class RestorableColor extends RestorableProperty<Color> {
Color _color = Colors.blue;
@override
Color createDefaultValue() => _color;
@override
void fromPrimitives(Object? data) {
final map = data as Map<String, dynamic>;
_color = Color(map['value'] as int);
}
@override
Object toPrimitives() {
return {'value': _color.value};
}
@override
Color get value => _color;
set value(Color newColor) {
if (_color != newColor) {
_color = newColor;
notifyListeners();
}
}
}
Register your custom RestorableColor just like built-ins, and the framework handles serialization.
Best Practices for State Restoration
• Use concise restorationIds – keep them unique within each scope.
• Persist only minimal UI state – avoid large data blobs in buckets.
• Handle asynchronous initialization – restore simple values first, then fetch remote data.
• Clean up obsolete buckets – if you rename or remove IDs, call dispose and remove stored fragments.
• Test restoration by simulating process death (e.g., Android Studio’s “Terminate Application” in DevTools).
Advanced Use Cases
• Nested Navigation: Assign restorationScopeId to Navigator widgets to persist their stacks separately.
• Scroll Position: Use ScrollController with RestorableScrollController to restore list offsets.
• Tab State: Wrap TabController in RestorableTabController for tab index persistence.
• Deep Links: Combine restoration with onGenerateInitialRoutes to rebuild link-based navigation.
Each advanced scenario uses the same pattern: mix in RestorationMixin, register RestorableProperty, and assign proper IDs.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Flutter’s state restoration framework empowers you to deliver seamless user experiences even after app restarts. By enabling restoration at the root level, registering built-in or custom RestorableProperty instances, and following best practices, you ensure critical UI state survives process termination. Leverage these tools to maintain counters, form inputs, navigation stacks, scroll positions, and more across sessions.
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.











