-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Description
Is there an existing issue for this?
- I have searched the existing issues
- I have read the guide to filing a bug
Steps to reproduce
- Run sample code.
- Press Go /a
- Press pop
- Press Push /a
- Press pop
- Check console log print statements
Expected results
Expect context.go(), context.push(), context.pop(), to update GoRouterState in a ShellRoute correctly.
Currently it looks like:
context.go() updates GoRouterState.
context.pop() from a context.go() partially updates GoRouterState(seems to only update .location).
context.push() does NOT update GoRouterState.
context.pop() from a context.push() does NOT update GoRouterState.
A correct GoRouterState in a shell route builder is especially important if you want to display an AppBar in the shell route builder that uses GoRouterState to update the AppBar title or dynamically show a navigation drawer or back button. (Like I show in the sample code)
Another use case would be to grab a param from the GoRouterState to load some logic or state in the shell route builder widget and pass it to a state management solution like Bloc, Inherited Widget or Riverpod ProviderScope to make it available to all child widgets.
I tested this on iOS and Android.
I do not have much experience using queryParams or extra so I did not check if they are updated correctly in GoRouterState during my testing.
Also GoRouterState.name always shows up at null in shell routes and I believe from previous discussions on here that is expected behavior as ShellRoutes don’t have a name parameter. I think that is understandable but I would expect the ShellRoute’s GoRouterState location, subloc, fullPath, params to be updated correctly and with all the params available from the current path.
Actual results
- Press Go /a -> GoRouterState updates correctly
- Press pop -> GoRouterState.location is correct (subloc, fullpath, params are incorrect)
- Press Push /a -> GoRouterState not updated and matches previous state (location, subloc, fullpath, params are incorrect)
- Press pop -> GoRouterState not updated and matches previous state
Code sample
Code sample
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(MyApp());
}
final shellKey = GlobalKey<NavigatorState>();
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
final _router = GoRouter(
initialLocation: '/',
debugLogDiagnostics: true,
routes: [
ShellRoute(
navigatorKey: shellKey,
builder: (context, state, child) => ShellScaffold(child: child),
pageBuilder: (context, state, child) =>
MaterialPage(child: ShellScaffold(child: child)),
routes: [
GoRoute(
name: 'Home',
path: '/',
builder: (context, state) => const Home(),
routes: [
GoRoute(
name: 'Page A',
path: 'a',
builder: (context, state) => const PageA(),
routes: [
GoRoute(
name: 'Page B',
path: 'b',
builder: (context, state) => const PageB(),
routes: [
GoRoute(
name: 'Page C',
path: ':id',
builder: (context, state) => PageCWithParam(
id: state.params['id']!,
),
),
],
),
],
),
],
),
],
)
],
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Shell Route State Test',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
routerConfig: _router,
);
}
}
class ShellScaffold extends StatelessWidget {
const ShellScaffold({Key? key, required this.child}) : super(key: key);
final Widget child;
@override
Widget build(BuildContext context) {
final state = GoRouterState.of(context);
print('ShellScaffold build');
print('GoRouter location: ${GoRouter.of(context).location}');
print('GoRouterState location: ${state.location}');
print('GoRouterState subloc: ${state.subloc}');
print('GoRouterState fullpath: ${state.fullpath}');
print('GoRouterState params: ${state.params}');
return Scaffold(
appBar: AppBar(
title: Text(state.location),
// automaticallyImplyLeading: true,
/// automaticallyImplyLeading does not seem work inside of a ShellRoute.
/// Manually defining leading below is then required to show a back button.
/// This is why having a correct GoRouterState is needed.
leading: (state.location.startsWith('/a'))
? BackButton(
onPressed: () => context.pop(),
)
: null,
actions: [
/// Used to reset the state of the current path and return to home.
IconButton(
onPressed: () => context.go('/'), icon: const Icon(Icons.home))
],
),
body: child,
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
const Text('Home'),
TextButton(
onPressed: () => context.go('/a'),
child: const Text('Go /a'),
),
TextButton(
onPressed: () => context.push('/a'),
child: const Text('Push /a'),
),
],
),
),
);
}
}
class PageA extends StatelessWidget {
const PageA({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
const Text('Page A'),
TextButton(
onPressed: () => context.go('/a/b'),
child: const Text('Go /a/b'),
),
TextButton(
onPressed: () => context.push('/a/b'),
child: const Text('Push /a/b'),
),
TextButton(
onPressed: () => context.pop(),
child: const Text('pop'),
),
],
),
),
);
}
}
class PageB extends StatelessWidget {
const PageB({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
const Text('Page B'),
TextButton(
onPressed: () => context.go('/a/b/1'),
child: const Text('Go /a/b/1'),
),
TextButton(
onPressed: () => context.push('/a/b/2'),
child: const Text('Push /a/b/2'),
),
TextButton(
onPressed: () => context.pop(),
child: const Text('pop'),
),
],
),
);
}
}
class PageCWithParam extends StatelessWidget {
const PageCWithParam({Key? key, required this.id}) : super(key: key);
final String id;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
Text('Page C with param: $id'),
TextButton(
onPressed: () => context.pop(),
child: const Text('pop'),
),
],
),
);
}
}
Flutter Doctor output
Doctor output
[✓] Flutter (Channel beta, 3.10.0-1.1.pre, on macOS 13.3.1 22E261 darwin-arm64, locale en-US)
• Flutter version 3.10.0-1.1.pre on channel beta at /Users/dalenjohnson/Developer/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision ecdb7841df (3 weeks ago), 2023-04-11 20:47:40 -0700
• Engine revision 870e640e7e
• Dart version 3.0.0 (build 3.0.0-417.1.beta)
• DevTools version 2.23.1
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
• Android SDK at /Users/dalenjohnson/Library/Android/sdk
• Platform android-33, build-tools 33.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 14.3)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 14E222b
• CocoaPods version 1.12.0
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2022.1)
• 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 11.0.15+0-b2043.56-8887301)
[✓] VS Code (version 1.77.3)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.62.0
[✓] Connected device (4 available)
• sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 13 (API 33) (emulator)
• iPhone 14 Pro Max (mobile) • 9B35DE3E-8844-43EA-ABC2-D440C9A957EE • ios • com.apple.CoreSimulator.SimRuntime.iOS-16-4 (simulator)
• macOS (desktop) • macos • darwin-arm64 • macOS 13.3.1 22E261 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 112.0.5615.137
[✓] Network resources
• All expected network resources are available.
• No issues found!]