Skip to content

[go_router] onEnter only triggers once (no recursive redirection) #188014

Closed
flutter/packages
#12337

Description

@jlnrrg

Steps to reproduce

  1. Use the Code Sample
  2. Add go_router to pubspec.yaml
  3. Run the App

The onEnter is only called once for the '/' Router, but not after the redirect for the '/a' route.
If you hit F5 refresh the onEnter is applied "again" and redirects to '/b'.

Related Issue: #178984
Related MR: flutter/packages#11108

Expected results

Page 'B' is visible.

Actual results

Page 'A' is visible

Code sample

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({super.key});

  final router = GoRouter(
    initialLocation: '/',
    onEnter: (context, currentState, nextState, goRouter) {
      if (nextState.path == '/a') {
        return Block.then(() => goRouter.go('/b'));
      }
      return Allow();
    },
    routes: [
      GoRoute(
        path: '/',
        redirect: (context, state) {
          return '/a';
        },
      ),
      GoRoute(
        path: '/a',
        builder: (_, _) => _TestWidget(identifier: 'A'),
      ),
      GoRoute(
        path: '/b',
        builder: (_, _) => _TestWidget(identifier: 'B'),
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
      routerConfig: router,
    );
  }
}

class _TestWidget extends StatelessWidget {
  const _TestWidget({super.key, required this.identifier});

  final String identifier;

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: Column(children: [Text(identifier)]));
  }
}

Screenshots or Video

No response

Logs

Logs
Flutter 3.38.9 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 67323de285 (5 months ago) • 2026-01-28 13:43:12 -0800
Engine • hash 5eb06b7ad5bb8cbc22c5230264c7a00ceac7674b (revision 587c18f873) (4
months ago) • 2026-01-27 23:23:03.000Z
Tools • Dart 3.10.8 • DevTools 2.51.1
go_router: ^17.3.0

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.38.9, on macOS 26.3.1 25D771280a darwin-arm64, locale
    en-DE)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.2)
[✓] Chrome - develop for the web
[✓] Connected device (2 available)
[✓] Network resources

• No issues found!

Activity

  1. added
    in triagePresently being triaged by the triage team
    frameworkflutter/packages/flutter repository. See also f: labels.
    f: routesNavigator, Router, and related APIs.
    packageflutter/packages repository. See also p: labels.
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    and removed
    in triagePresently being triaged by the triage team
    on Jun 15, 2026
  2. mbcorona commented on Jun 15, 2026

    @mbcorona
    Member

    Hi @jlnrrg , thanks for reporting. Routing to the framework team for evaluation.

  3. added
    P2Important issues not at the top of the work list
    on Jun 23, 2026
  4. Dhritimann commented on Jul 17, 2026

    @Dhritimann

    Hi, I'd like to take this one if it's not already assigned.
    From reading through router.dart and configuration.dart, my understanding of the root cause is : the documented order of operations is onEnter → top-level redirect → route-level GoRoute.redirect, and onEnter is only evaluated once, against the original location, before the redirect loop resolves. When a route-level redirect (e.g. / → /a) changes the matched location, onEnter isn't re-invoked against the new location, so a guard meant to intercept /a never fires. This looks like the same class of issue that #178984 / PR #11108 addressed for top-level redirect re-evaluation, but it doesn't seem to extend to onEnter.
    My plan is to:

    reproduce locally with the sample from this issue
    modify the redirect-resolution loop in configuration.dart so onEnter is re-run against the newly resolved GoRouterState whenever a redirect changes the location, reusing the existing redirectLimit guard to prevent infinite loops
    add a regression test in test/redirects_test.dart (or a dedicated onEnter test) covering this exact repro

    Let me know if this direction sounds right or if there's context I'm missing before I start on the PR.

  5. developerashkan commented on Aug 1, 2026

    @developerashkan

    Traced this against main — turns out this is documented, intended behavior rather than a bug. onEnter is evaluated once against the literal navigated-to URI, strictly before both legacy top-level redirect: and GoRoute.redirect: run, and it's never re-invoked as those redirects resolve intermediate hops. There's actually an existing regression test ('onEnter called once when top-level redirect chains', written for the related
    #178984) that asserts this exact ordering for the same / → /a → /b pattern.

    So next.uri.path will never equal /a here, because onEnter only ever sees the pre-redirect target (/). The fix is to put the guard inside a redirect: callback instead of onEnter — redirect: does see each resolved hop, since it's what produces them.

    Since this is intentional and already covered by tests, I don't think there's a code bug to fix, but the doc comment didn't make this visibility gap explicit, so opened a small PR clarifying it and adding a regression test for this exact scenario plus one showing the workaround: flutter/packages#12337

  6. jlnrrg commented on Aug 6, 2026

    @jlnrrg
    Author

    Thank you for your effort of researching this "bug".
    I would still question if this is supposed be the intended behaviour, as onEnter was at one point supposed to replace the redirect method.
    As far as I can see @omar-hanafy wanted to keep the deprecated redirect only for backwards compatibility flutter/packages#8339 (comment).
    I am unsure though if that changed further down the line.

  7. Dhritimann commented on Aug 7, 2026

    @Dhritimann

    Thanks for digging into this @developerashkan — makes sense. I'll hold off on a code change until the onEnter-vs-redirect precedence question is settled by maintainers. Happy to help with the regression tests on #12337 if useful.

  8. jlnrrg commented on Aug 25, 2026

    @jlnrrg
    Author

    Thank you for the clarification doc addition @developerashkan, but tbh I don't think this issue should be closed, as we are still waiting for feedback.
    As I was not a contributior, I'm not allowed to reopen this issue though.

  9. github-actions commented on Sep 8, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  10. locked as resolved and limited conversation to collaborators on Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listf: routesNavigator, Router, and related APIs.frameworkflutter/packages/flutter repository. See also f: labels.has 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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions