2

so I have an app which, let's say, consist of just a home screen, a profile screen and a basket screen. From the main/home screen, I can get to both other screens. Actually, I can access all of those three screens via a sidebar. If I start the app in the debug mode I can navigate as follows:

HomeScreen -> ProfileScreen -> HomeScreen (over sidebar) -> Profile Screen.

All of those are done via Navigator.pushNamed(). This means, the _history stack of the Navigator has:

  • HomeScreen
  • ProfileScreen
  • HomeScreen
  • Profile Screen

Which is kinda dumb and it uses RAM without the need to do so. I could fix that problem by just using popUntil() every time I get to the HomeScreen so that I have a fresh history but that wouldn't help if I do something like:

HomeScreen -> ProfileScreen -> BasketScreen (over sidebar) -> Profile Screen (over sidebar) -> BasketScreen (over sidebar).

How do I get rid of the RIGHT screens from the history, ideally without making my own Navigator? Is it possible at all? I mean, ideally, the history of the Navigator after the last example should look like this:

  • HomeScreen
  • Profile Screen
  • BasketScreen

(meaning if the BasketScreen is called, it removed all previous BasketScreens from the history, etc.)

Or is it just my mistake to allow such cyclic calls in the first place? Though I doubt I'm the only one to use Sidebars like that.

Panossa
  • 371
  • 5
  • 17
  • I guess if you are worried about it you could look at pop before a push. You can override the page route to suppress the animation for the pop so it would look similar to what you had before https://stackoverflow.com/questions/49874272/how-to-navigate-to-other-page-without-animation-flutter – Chazman Jul 30 '20 at 13:57
  • You can removeRoute(routeName) before calling pushNamed(). It throws an error if the page doesn't exist (which can be ignored in your case). However, this way none of your pages can utilize the value passed by the popped page because they get disposed. – Navaneeth P Jul 30 '20 at 14:18
  • @NavaneethP sadly I'm too dumb to use removeRoute outside of the typical context. We're using an extra class "ScreenController" to manage route transitions and I can't find out how to get the second argument required by removeRoute (of type Route). – Panossa Jul 30 '20 at 15:22
  • @Chazman the problem is that pop only removes the top element of the stack but if I go like BasketScreen->ProfileScreen->BasketScreen, I'd like to remove the old BasketScreen from the stack which is unaccessible without removing the ProfileScreen, am I right? – Panossa Jul 30 '20 at 15:24
  • are u using all named routes? – Navaneeth P Jul 30 '20 at 15:36
  • Admittedly, this is not something I've spent much time considering. I don't know if there is a way to peek into the actual nav stack so you may need to build your own stack to know where you're at. In your example above, you'd have to know that BasketScreen is the parent of ProfileScreen and instead of pushing to BasketScreen a second time you'd, instead, pop. But it's probably best if you consulted flutter docs https://api.flutter.dev/flutter/widgets/Navigator-class.html – Chazman Jul 30 '20 at 15:37
  • @NavaneethP Yes, all routes we use are named. – Panossa Jul 30 '20 at 15:43
  • @Chazman There's a way to peek into the nav stack by adding a NavigatorObserver in the runApp() method call. But that seems way too complicated for a removal of duplicates since you need to track every movement in that stack and act accordingly without ever being able to access the actual nav stack with write access. – Panossa Jul 30 '20 at 15:45

1 Answers1

-1

I recently came across this same issue, in addition I had the possibility of routes needing to be deleted later. To fix the problem of duplicate pages, simply use

Navigator.pushReplacementNamed(context, name)

instead of

Navigator.pushNamed(context, name)

If you need a bit more control and context for the routes, the only real way to do this is to create a custom NavigatorObserver, adding it to your MaterialApp, and then referencing it whenever you need to remove a page (as if you try to remove a route without it actually being in the Navigator's history, it will throw an error). This NavigatorObserver should work for most purposes.

One thing to note is it seems you CANNOT remove routes inside of the NavigatorObserver as it seems to lock itself to changes while changes are being made.

class Observatory extends NavigatorObserver{

  List<Route> routeHistory = List();

  @override
  void didPush(Route route, Route previousRoute) {
    routeHistory.add(route);
    super.didPush(route, previousRoute);
  }

  @override
  void didPop(Route route, Route previousRoute) {
    routeHistory.removeLast();
    super.didPop(route, previousRoute);
  }

  @override
  void didRemove(Route route, Route previousRoute) {
    var beginIndex = routeHistory.indexOf(route);
    var endIndex = routeHistory.indexOf(previousRoute);
    if((endIndex == -1 && beginIndex != -1) || beginIndex -1 == endIndex)
      routeHistory.remove(route);
    else if(endIndex != -1 && beginIndex != -1)
      routeHistory.removeRange(beginIndex,endIndex);
    super.didRemove(route, previousRoute);
  }

  @override
  void didReplace({Route newRoute, Route oldRoute}) {
    if(routeHistory.contains(oldRoute))
      routeHistory[routeHistory.indexOf(oldRoute)] = newRoute;
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
  }

  bool containsRoute(Route route) => routeHistory.contains(route);
}

Now whenever I need to remove a route I do

if(observatory.containsRoute(route))
    Navigator.remove(context, route);
  • For my project I "fixed" the issue by just making a custom navigator which would empty itself (or its stack) each time a user gets to the home screen. Since the user will get there eventually, it's not that big of a problem. ^^' – Panossa Oct 28 '20 at 18:03