How to get current route path in Flutter?
Asked Answered
R

16

109

While implementing persistent bottom bar, previous route need to be restored when a button in the bottom bar was clicked.

When a button in the bottom bar is clicked, its current route path (/a/b/c) is saved and previously saved route is restored according to the button click.

Conceptually user will think each button as a workspace and its state is never get lost (including back stack). User can safely switch from one workspace to another.

How to get current route path in Flutter, when the routing is rewinding to root?

Raouf answered 29/9, 2017 at 7:33 Comment(1)
Many things were added in Navigator 2.0: medium.com/flutter/… For new apps consider a whole different design.Quartus
C
174

ModalRoute should give you the exact route name

ModalRoute.of(context).settings.name

Note: if you use Navigator.popUntil.. check this other answer out by Rémi Rousselet https://mcmap.net/q/196550/-how-to-check-which-the-current-route-is

Chargeable answered 2/3, 2019 at 19:8 Comment(4)
Why to you have import of the path package in your sample? Does it have anything to do with the solution?Paleo
It does not return the current route on the navigatorEsdras
This doesn't work after using Navigator.popuntil....Casi
This doesnt work because I am using onGenerateRoute https://mcmap.net/q/196551/-difference-between-ongenerateroute-and-routes-in-flutter . This can be fixed by passing settings though: github.com/flutter/flutter/issues/50192#issuecomment-590307455Tehee
O
42

In case if you want to get current route by use navigator key, possible to use popUntil method for it:

String? currentPath;
navigatorKey.currentState?.popUntil((route) {
  currentPath = route.settings.name;
  return true;
}); 
Observer answered 8/8, 2021 at 18:6 Comment(5)
Perfect answer when using nested navigator key, well play !!!Pudding
I couldn't figure out why all my returned names are null. Then I found out that I needed to pass the 'settings' property when building the routes manually using onGenerateRoute method and returning MaterialPageRoute( builder: (context) => YourWidget(), settings: RouteSettings(name: "payment") )Venable
This is a sweet hack, take my +1Courtenay
That is a simple but genial hack!Marcusmarcy
@OliverDixon This will not pop routes because function returns true.Observer
C
31

NavigatorState doesn't expose an API for getting the path of the current route, and Route doesn't expose an API for determining a route's path either. Routes can be (and often are) anonymous. You can find out if a given Route is on the top of the navigator stack right now using the isCurrent method, but this isn't very convenient for your use case.

I would recommend that you take a different approach to this problem and don't rewind to the root at all. Instead, use a different Navigator widget for each pane of the BottomNavigationBar. That way, you won't have to rewind the stack when switching between panes. You can wrap your Navigator widgets in Opacity and IgnorePointer widgets to hide them when they aren't supposed to be visible without destroying their stack.

app video

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class SecurePage extends StatelessWidget {
  final int index;

  SecurePage(this.index);

  Widget build(BuildContext context) {
    return new Material(
      color: Colors.amber,
      child: new InkWell(
        child: new Center(
          child: new Icon(
            Icons.security,
            color: Colors.white,
            size: index * 100.0 + 20.0,
          ),
        ),
        onTap: () {
          Navigator.of(context).push(
            new MaterialPageRoute(
              builder: (BuildContext context) {
                return new SecurePage(index + 1);
              },
            ),
          );
        },
      ),
    );
  }
}

class VerifiedPage extends StatelessWidget {
  final int index;

  VerifiedPage(this.index);

  Widget build(BuildContext context) {
    return new Material(
      color: Colors.green,
      child: new InkWell(
        child: new Center(
          child: new Icon(
            Icons.verified_user,
            color: Colors.white,
            size: index * 100.0 + 20.0,
          ),
        ),
        onTap: () {
          Navigator.of(context).push(
            new MaterialPageRoute(
              builder: (BuildContext context) {
                return new VerifiedPage(index + 1);
              },
            ),
          );
        },
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  int _page = 0;
  List<Widget> initialWidgets = <Widget>[
    new SecurePage(0),
    new VerifiedPage(0),
  ];

  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Stack(
        children: new List<Widget>.generate(initialWidgets.length, (int index) {
          return new IgnorePointer(
            ignoring: index != _page,
            child: new Opacity(
              opacity: _page == index ? 1.0 : 0.0,
              child: new Navigator(
                onGenerateRoute: (RouteSettings settings) {
                  return new MaterialPageRoute(
                    builder: (_) => initialWidgets[index],
                  );
                },
              ),
            ),
          );
        }),
      ),
      bottomNavigationBar: new BottomNavigationBar(
        currentIndex: _page,
        onTap: (int index) {
          setState(() {
            _page = index;
          });
        },
        items: <BottomNavigationBarItem>[
          new BottomNavigationBarItem(
            icon: new Icon(Icons.security),
            title: new Text('Secure'),
          ),
          new BottomNavigationBarItem(
            icon: new Icon(Icons.verified_user),
            title: new Text('Verified'),
          ),
        ],
      ),
    );
  }
}
Crime answered 30/9, 2017 at 0:57 Comment(4)
Wow! Beautify. This solution also solve other state problem I am having.Raouf
In fact, I was tinkering around Stack and Opacity, but not able to get it work. I was not aware Navigator can be localised. IgnorePointer is another trick I was not able to figure out. Combining the two, a build-in Hide-Show Widget will be good, to make them discoverable.Raouf
This fantastic solution has other issue.Raouf
@CollinJackson What happens if IgnorePointer isn't included? I read the documentation but am still unsure how it relates to the navigation stackTransposal
P
25

If you are using go_router package you can use the below code :

GoRouter.of(context).location

Update on go_router 10.0.0 (2023) :

GoRouterState.of(context).uri.toString();
Plantain answered 1/9, 2022 at 21:9 Comment(2)
If you don't have the context, you can also use the navigatorKey: final currentPath = GoRouter.of(navigatorKey.currentContext!).location;Chemisorption
What's the difference from uri.toString() and routeInformationProvider.value.location?Xanthene
M
15

Posting this answer mainly for archival purposes, but as @ikben mentioned, one way to get the current route, and all its properties, is ModalRoute.of(context). This returns a ModalRoute, which despite its name, applies to most Navigator.push calls, not just showDialog. Helpful properties include Route.settings, Route.navigator, and Route.isFirst.

Martres answered 19/8, 2019 at 20:54 Comment(0)
E
6

If you are using the go_router package version >= 9.0.0:

GoRouter.of(context).routeInformationProvider.value.location

or

GoRouterState.of(context).location
Efficiency answered 12/7, 2023 at 10:21 Comment(0)
O
5

I was using onGenerateRoute so I also had this issue. I resolved it by using NavigatorObserver

import 'package:flutter/material.dart';

class AppNavObserver extends NavigatorObserver {
  static final navStack = <RouteStackItem>[];

  @override
  void didPop(Route route, Route? previousRoute) {
    if (previousRoute != null) {
      navStack.removeLast();
    }
    super.didPop(route, previousRoute);
  }

  @override
  void didPush(Route route, Route? previousRoute) {
    navStack.add(RouteStackItem.fromRoute(route));
    super.didPush(route, previousRoute);
  }

  @override
  void didRemove(Route route, Route? previousRoute) {
    if (previousRoute != null) {
      navStack.removeLast();
    }
    super.didRemove(route, previousRoute);
  }

  @override
  void didReplace({Route? newRoute, Route? oldRoute}) {
    if (oldRoute != null) {
      navStack.removeLast();
    }
    if (newRoute != null) {
      navStack.add(RouteStackItem.fromRoute(newRoute));
    }
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
  }

  @override
  void didStartUserGesture(Route route, Route? previousRoute) {
    // TODO: implement didStartUserGesture
    super.didStartUserGesture(route, previousRoute);
  }

  @override
  void didStopUserGesture() {
    // TODO: implement didStopUserGesture
    super.didStopUserGesture();
  }
}

class RouteStackItem {
  final String? name;
  final Object? args;

  const RouteStackItem({
    required this.name,
    required this.args,
  });

  factory RouteStackItem.fromRoute(Route route) =>
      RouteStackItem(name: route.settings.name, args: route.settings.arguments);
}

In main.dart

MaterialApp(
              debugShowCheckedModeBanner: false,
              title: flavorConfig.appTitle,
              color: AppColors.primary,
              theme: AppTheme.light,
              navigatorObservers: [
                AppNavObserver()
              ],.....

Then you can use this to get the current route from anywhere

   var navStack = AppNavObserver.navStack;
          var routeSettings = navStack.isEmpty ? null : navStack.last;
Oringa answered 17/5, 2023 at 6:28 Comment(1)
Absolutely fantastic!Cooks
P
3

I'm using go_router in my project. In go_router, you access current route as below

    var currentRoute = GoRouter.of(context).location;
Ptisan answered 20/12, 2022 at 12:43 Comment(0)
V
3

If you are using go_router ^11, use

router.routeInformationProvider.value.uri
Vacant answered 13/10, 2023 at 6:40 Comment(0)
S
2

If you're using go_router:

GoRoute(
  name: 'login',
  path: '/login',
  builder: (context, state) => LoginScreen(),
),

use this

GoRouterState.of(context).path

for named routes use:

GoRouterState.of(context).name
Shallow answered 1/2, 2023 at 10:17 Comment(0)
C
1

I found a much simpler solution. I love StatelessWidget's so I did it with a static, but you can do it with a StatefulWidget. You'll need a StatefulWidget if you have hierarchical navigation. This is a tile in my NavDrawer. (Statics get a bad rap but in a UI it's not bad, there's only one process running a single thread.)

class NavListTile extends StatelessWidget {
  static String currentRoute = 'dashboard';
  final String route;

  const NavListTile({
    Key? key,
    required this.route,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    var isCurrent = currentRoute == route;
    String suffix = isCurrent ? '_green' : '';
    return ListTile(
      title: NavMenuItem(capitalizedRoute, isCurrent,
          "assets/images/" + route + suffix + ".png", context),
      onTap: () {
        currentRoute = route;
        Navigator.of(context).pushNamed('/' + route);
      },
    );
  }
}
Calhoun answered 25/6, 2021 at 14:24 Comment(0)
V
1

You could write a convenient extension on NavigatorState to get the first route in the stack:

extension NavigatorStateX on NavigatorState {

  String? get currentRouteName {
    String? name;
    popUntil((route) {
      name = route.settings.name;
      return true;
    });
    return name;
  }

}
Vibrio answered 16/6, 2023 at 19:41 Comment(0)
N
1

I didn't have context available but was able to import the router from the file it was defined in and use like this:

router.routeInformationProvider.value.location
Nigeria answered 20/8, 2023 at 8:2 Comment(2)
In v10 it's: router.routeInformationProvider.value.uriWayside
I actually noticed a bug with this, it seems like it did't update. I opened an issue but haven't been able to update it yet with an example so they closed it.Nigeria
M
1

This works for me

final routeName = Navigator.of(context).widget.pages.last.name;
Milburr answered 1/10, 2023 at 9:24 Comment(1)
That's the only solution worked for me, thanksPenthouse
B
0

For go_router: >13.0.1

with context:

final currentPath = 
GoRouter.of(context).routeInformationProvider.value.uri.path

with Global / Navigator Key:

final currentPath =  GoRouter.of(goRouterKey.currentContext!).routeInformationProvider.value.uri.path

inside GoRouter redirect:

GoRouter(redirect: (context, state) {final currentPath =  state.uri.path}
Blaisdell answered 15/1, 2024 at 13:44 Comment(0)
S
0

ModalRoute.of(context).settings.name should be one of the easy ways to get the route name, as mentioned in previous answers.

But, it was giving me null for some routes. To solve this, I had to do the following.

I am using onGenerateRoute to register AppRoutes in MyApp.

My AppRoutes class was something like this:

class AppRoutes {
  static Route onGenerateRoutes(RouteSettings routeSettings) {
    switch (routeSettings.name) {
      case "/":
        return _materialPageRoute(const News());
      case "/saved_articles":
        return _materialPageRoute(const SavedArticles());
      default:
        return _materialPageRoute(const News());
    }
  }

  static Route _materialPageRoute(Widget page) {
    return MaterialPageRoute(builder: (context) => page);  // RouteSettings was not included
  }
}

Then, I made changes to include RouteSettings to MaterialPageRoute. This solves the problem of null value for route names.

class AppRoutes {
  static Route onGenerateRoutes(RouteSettings routeSettings) {
    switch (routeSettings.name) {
      case "/":
        return _materialPageRoute(const News(), routeSettings);
      case "/saved_articles":
        return _materialPageRoute(const SavedArticles(), routeSettings);
      default:
        return _materialPageRoute(const News(), routeSettings);
    }
  }

  static Route _materialPageRoute(Widget page, RouteSettings routeSettings) {
    return MaterialPageRoute(
        settings: routeSettings, builder: (context) => page); // RouteSettings is now included
  }
}
Skijoring answered 5/5, 2024 at 18:14 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.