Skip to content

[Bug] pushNamed duplicates route in stack when RouteGuard redirects to an already-existing route #1045

@anagamplay

Description

@anagamplay

Description:
When a RouteGuard blocks navigation and redirects via redirectTo, the redirect is resolved internally by selectBook() through _routeSuccess(), which replaces the RedirectRoute with the actual target route before returning the ModularBook. By the time pushNamed receives this book, it has no way to know a redirect occurred.

The pushNamed logic then tries to add the resolved route to the current stack. If that route already exists (e.g., the user is already on /login/ and tries to access a guarded route), the deduplication loop skips it — but the fallback on the next line unconditionally forces it in anyway:

// modular_router_delegate.dart
if (currentConfiguration!.routes.length == list.length) {
  list.add(book.routes.last); // ← forces duplicate
}

This results in a stack like [/login/, /login/], which causes a spurious back button to appear on the home screen AppBar.

Steps to Reproduce:

  1. Create an app with flutter_modular and define a guarded route:
ModuleRoute('/admin/', module: AdminModule(), guards: [AuthGuard()])
  1. Implement the guard with redirectTo:
class AuthGuard extends RouteGuard {
  AuthGuard() : super(redirectTo: '/login/');

  @override
  Future<bool> canActivate(String path, ModularRoute route) async {
    return Modular.get<AuthController>().isLoggedIn; 
  }
}
  1. While already on /login/, trigger:
Modular.to.pushNamed('/admin/dashboard');

(This happens naturally via push notification deep links or any external navigation attempt.)

Observe the AppBar on /distributor/home/ now has a back arrow pointing to an identical /distributor/home/ page.

Expected behavior
When a RouteGuard redirects to a route that already exists in the navigation stack, the stack should remain clean — no duplicate entries, no spurious back button.

Actual Behavior:
The redirected route is duplicated in the stack ([/login/, /login/]), causing a back button to appear on the home screen. Pressing it navigates to an identical copy of the same page.

Root Cause:

In modular_router_delegate.dart, pushNamed cannot distinguish between:

  • A redirect that resolved to an existing route (should replace the stack)
  • An intentional push of the same route with different arguments (should stack)
    The fallback list.add(book.routes.last) was designed for the second case but incorrectly fires in the first.

Suggested Fix:

Detect whether the resolved route differs from the originally requested one. If it does, a guard redirect occurred and setNewRoutePath should be used instead of accumulating:

Future<T?> pushNamed<T extends Object?>(String routeName, ...) async {
  // ...
  final requestedPath = Uri.parse(routeName).path.replaceAll(RegExp(r'/$'), '');
  final resolvedPath = book.routes.last.uri.path.replaceAll(RegExp(r'/$'), '');
  final wasRedirected = requestedPath != resolvedPath;

  if (wasRedirected) {
    await setNewRoutePath(book);
    return await popComplete.future;
  }

  // existing push logic with deduplication loop + fallback...
}

Environment:
flutter_modular: 6.3.4
Flutter: 3.35.4

Note:
I would like to work on a fix for this issue. I'm available to submit a PR with the suggested approach above, or an alternative if the maintainers prefer a different solution.

Metadata

Metadata

Assignees

No one assigned

    Labels

    newNew issue request attention

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions