Skip to content

[go_router] Chained/Recursive redirection stops after the first redirect in v16+ #178984

Description

@yehorh

Steps to reproduce

  1. Use go_router version ^16.0.0 or ^17.0.0.
  2. Define a top-level redirect function that handles a chain:
    • If path is / -> return /a
    • If path is /a -> return /b
  3. Run the app starting at /.

Expected results

In version 15, if a top-level redirect returned a new path, the router would immediately evaluate the redirect function again against that new path. This allowed for chaining redirects (e.g., / -> /a -> /b).

https://pub.dev/documentation/go_router/latest/topics/Redirection-topic.html

Actual results

In version 16+, the router stops processing after the first redirect occurs. It navigates to the intermediate route instead of continuing the chain to the final destination.

Code sample

Code sample
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(const App());
}

class App extends StatefulWidget {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  late final GoRouter _router;

  @override
  void initState() {
    _router = GoRouter(
      redirect: (context, state) {
        print('redirect called with location: ${state.matchedLocation}');
        if (state.matchedLocation == '/') {
          print('redirecting to /a');
          return '/a';
        } else if (state.matchedLocation == '/a') {
          print('redirecting to /b');
          return '/b';
        } else {
          print('no redirect');
          return null;
        }
      },
      routes: [
        GoRoute(path: '/', builder: (context, state) => const RScreen()),
        GoRoute(path: '/a', builder: (context, state) => const AScreen()),
        GoRoute(path: '/b', builder: (context, state) => const BScreen()),
      ],
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(routerConfig: _router);
  }
}

class RScreen extends StatelessWidget {
  const RScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('r'));
  }
}

class AScreen extends StatelessWidget {
  const AScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('a'));
  }
}

class BScreen extends StatelessWidget {
  const BScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('b'));
  }
}

Screenshots or Video

Logs

Expected Behavior (observed in v15):
The app should redirect recursively until null is returned.
Logs from v15:

I/flutter ( 2159): redirect called with location: /
I/flutter ( 2159): redirecting to /a
I/flutter ( 2159): redirect called with location: /a
I/flutter ( 2159): redirecting to /b
I/flutter ( 2159): redirect called with location: /b
I/flutter ( 2159): no redirect

The screen displays "b".

Actual Behavior (observed in v16 - v17):
The redirection stops after the first step.
Logs from v16/17:

I/flutter ( 2159): redirect called with location: /
I/flutter ( 2159): redirecting to /a

The screen displays "a".

Flutter Doctor output

Doctor output
sh 2972  (git)-[main]-% flutter doctor -v
[✓] Flutter (Channel stable, 3.38.2, on macOS 26.1 25B78 darwin-arm64, locale en-US) [490ms]
    • Flutter version 3.38.2 on channel stable at /Users/yehorh/opt/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f5a8537f90 (5 days ago), 2025-11-18 09:27:21 -0500
    • Engine revision b5990e5ccc
    • Dart version 3.10.0
    • DevTools version 2.51.1
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, omit-legacy-version-file,
      enable-lldb-debugging

[✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [2.4s]
    • Android SDK at /Users/yehorh/Library/Android/sdk
    • Emulator version 36.2.12.0 (build_id 14214601) (CL:N/A)
    • Platform android-36, build-tools 36.0.0
    • ANDROID_HOME = /Users/yehorh/Library/Android/sdk
    • Java binary at: /Users/yehorh/Library/Java/JavaVirtualMachines/azul-17.0.12/Contents/Home/bin/java
      This JDK is specified in your Flutter configuration.
      To change the current JDK, run: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment Zulu17.52+17-CA (build 17.0.12+7-LTS)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 26.1.1) [1,217ms]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 17B100
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [13ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Connected device (4 available) [6.4s]
    • Android SDK built for arm64 (mobile) • emulator-5554             • android-arm64  • Android 14 (API 34) (emulator)
    • yiPhone (mobile)                     • 00008101-000E28880AD2001E • ios            • iOS 26.1 23B85
    • macOS (desktop)                      • macos                     • darwin-arm64   • macOS 26.1 25B78 darwin-arm64
    • Chrome (web)                         • chrome                    • web-javascript • Google Chrome 142.0.7444.176
    ! Error: Yehor's Apple Watch 📟 has not finished loading development services. Please select a different device, or wait for the device to load development services and try again. (code 8)

[✓] Network resources [1,081ms]
    • 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 listc: regressionIt was better in the past than it is nowfound in release: 3.38Found to occur in 3.38found in release: 3.39Found to occur in 3.39has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: go_routerThe go_router packagepackageflutter/packages repository. See also p: labels.team-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions