-
Notifications
You must be signed in to change notification settings - Fork 29.5k
Open
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listfound in release: 3.35Found to occur in 3.35Found to occur in 3.35found in release: 3.36Found to occur in 3.36Found to occur in 3.36has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onp: go_routerThe go_router packageThe go_router packagepackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.team-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team
Description
Steps to reproduce
- Create a
GoRouterinstance - Put a Page inside a
GoRoute - Add restoration scope somewhere inside the page
- Wrap the
GoRoutewith AShellRoute
Expected results
Restoration logic serializes and deserializes the data
Actual results
Restoration logic only serializes the data, the page starts from scratch when android restores the state. The logic works fine in case we remove the ShellRoute
Code sample
Code sample
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
final routerConfig = GoRouter(
restorationScopeId: 'go_router_scope_id',
initialLocation: '/',
routes: [
ShellRoute(
restorationScopeId: 'go_router_scope_id_for_shell_route',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const MyHomePage(),
),
],
builder: (context, state, child) {
return RestorationScope(
restorationId: 'custom_restoration_scope',
child: child,
);
},
),
],
);
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
restorationScopeId: 'app_scope',
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
routerConfig: routerConfig,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with RestorationMixin {
final RestorableCounterState _counter = RestorableCounterState();
@override
String? get restorationId => 'home_page';
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_counter, 'counter_state');
}
void _incrementCounter() {
_counter.value.counter = CounterDto(
_counter.value.counter.counter + 1,
_counter.value.counter.counterName,
);
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: _counter.value,
child: Scaffold(
appBar: AppBar(title: const Text('Title')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16),
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Counter name',
),
onChanged: (value) {
_counter.value.counter = CounterDto(
_counter.value.counter.counter,
value,
);
},
),
),
const Text('You have pushed the button this many times:'),
Consumer<CounterState>(
builder: (context, state, _) {
return Text(
'${state.counter.counter}',
style: Theme.of(context).textTheme.headlineMedium,
);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
child: const Icon(Icons.add),
),
),
);
}
}
class RestorableCounterState extends RestorableChangeNotifier<CounterState> {
@override
CounterState createDefaultValue() {
print('RestorableCounterState -> Creating default value');
return CounterState();
}
@override
CounterState fromPrimitives(Object? data) {
print('RestorableCounterState -> Restoring $data');
final map = data as Map;
final json = map.cast<String, dynamic>();
return CounterState.fromValue(CounterDto.fromJson(json));
}
@override
Object? toPrimitives() {
print('RestorableCounterState -> Serializing ${value.counter}');
return value.counter.toJson();
}
}
class CounterState extends ChangeNotifier {
CounterState();
CounterState.fromValue(this._counter);
CounterDto _counter = CounterDto(0, 'N/A');
CounterDto get counter => _counter;
set counter(CounterDto value) {
_counter = value;
notifyListeners();
}
}
class CounterDto {
final int counter;
final String counterName;
CounterDto(this.counter, this.counterName);
@override
String toString() => 'counter: $counter; name: $counterName';
factory CounterDto.fromJson(Map<String, dynamic> json) => CounterDto(
json['counter'],
json['counterName'],
);
Map<String, dynamic> toJson() => {
'counter': counter,
'counterName': counterName,
};
}
Screenshots or Video
Screenshots / Video demonstration
[Upload media here]
Logs
Logs
[Paste your logs here]Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.35.2, on macOS 15.6.1 24G90 darwin-arm64, locale en-US) [827ms]
• Flutter version 3.35.2 on channel stable at /Users/ilias/Library/Flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 05db968908 (10 days ago), 2025-08-25 10:21:35 -0700
• Engine revision a8bfdfc394
• Dart version 3.9.0
• DevTools version 2.48.0
• Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations,
enable-lldb-debugging
[✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [2.0s]
• Android SDK at /Users/ilias/Library/Android/sdk
• Emulator version 36.1.9.0 (build_id 13823996) (CL:N/A)
• Platform android-36, build-tools 36.0.0
• ANDROID_HOME = /Users/ilias/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
This is the JDK bundled with the latest Android Studio installation on this machine.
To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
• Java version OpenJDK Runtime Environment (build 21.0.7+-13880790-b1038.58)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 16.4) [1,739ms]
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 16F6
• CocoaPods version 1.16.2
[✓] Chrome - develop for the web [73ms]
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2025.1) [72ms]
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 21.0.7+-13880790-b1038.58)
[✓] IntelliJ IDEA Community Edition (version 2025.1.4.1) [72ms]
• IntelliJ at /Applications/IntelliJ IDEA CE.app
• Flutter plugin version 86.0.2
• Dart plugin version 251.27812.12
[✓] Connected device (5 available) [6.9s]
• Pixel 9 (mobile) • 192.168.1.58:5555 • android-arm64 • Android 16 (API 36)
• sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 16 (API 36) (emulator)
• Ilias (wireless) (mobile) • 00008110-000E04CE3E2B801E • ios • iOS 18.6.2 22G100
• macOS (desktop) • macos • darwin-arm64 • macOS 15.6.1 24G90 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 140.0.7339.80
[✓] Network resources [764ms]
• All expected network resources are available.
• No issues found!jhlee-atmsads
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listfound in release: 3.35Found to occur in 3.35Found to occur in 3.35found in release: 3.36Found to occur in 3.36Found to occur in 3.36has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onp: go_routerThe go_router packageThe go_router packagepackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.team-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team