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
    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