Skip to content

[go_router] GoRouterState incorrect for ShellRoute on .push and .pop #125752

@dalenjohnson

Description

@dalenjohnson

Is there an existing issue for this?

Steps to reproduce

  1. Run sample code.
  2. Press Go /a
  3. Press pop
  4. Press Push /a
  5. Press pop
  6. 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

  1. Press Go /a -> GoRouterState updates correctly
  2. Press pop -> GoRouterState.location is correct (subloc, fullpath, params are incorrect)
  3. Press Push /a -> GoRouterState not updated and matches previous state (location, subloc, fullpath, params are incorrect)
  4. 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!]

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listfound in release: 3.10Found to occur in 3.10found in release: 3.7Found to occur in 3.7has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: go_routerThe go_router packagepackageflutter/packages repository. See also p: labels.r: fixedIssue is closed as already fixed in a newer version

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions