215

I want to develop a logout button that will send me to the log in route and remove all other routes from the Navigator. The documentation doesn't seem to explain how to make a RoutePredicate or have any sort of removeAll function.

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
chrislondon
  • 12,487
  • 5
  • 26
  • 65

16 Answers16

470

I was able to accomplish this with the following code:

Navigator.of(context)
    .pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);

The secret here is using a RoutePredicate that always returns false (Route<dynamic> route) => false. In this situation it removes all of the routes except for the new /login route I pushed.

chrislondon
  • 12,487
  • 5
  • 26
  • 65
  • 1
    Thanks a lot it worked successfully. Could you please send some link, where I can understand more.... – Pawan Feb 03 '19 at 07:00
  • I got a problem with this! On iOS, by using a native view as widget, after use your method the native view does not dispose properly and continue to exist, as a result i have a view that will be never used again running in a sort of background and impossible to dispose. This seems to happen just on iOS devices. – Lorenzo Imperatrice Apr 29 '19 at 15:37
  • 1
    @LorenzoImperatrice Did you found fix for this issue? I'm having similar issue – Mateusz Tylman Sep 12 '19 at 15:26
  • 3
    I still have instances open even after using this. any other solution to push screen and remove rest of the stack? – Manoj MM Nov 28 '19 at 08:47
  • 2
    How can we pass arguments with this approach. – Developine Dec 17 '19 at 13:40
  • 1
    I came here again year later after the first time saw it. Found answer helpful again and tried to upvote it. So sad that I can't upvote it twice! – Vlad Hudnitsky Jun 03 '20 at 21:05
  • 3
    @Developine I know it's quite late, but in case anyone else needs to pass arguments it can be done this way `Navigator.of(context).pushNamedAndRemoveUntil('/login', (Route route) => false, arguments: { "key": "value"});` – selected Mar 30 '21 at 11:44
  • what happen is return true? `(Route route) => true` – BIS Tech May 23 '21 at 17:01
175

I can do that using the following code snippet :

 Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) =>
    LoginScreen()), (Route<dynamic> route) => false);

if you want to remove all the route below the pushed route, RoutePredicate always return false, e.g (Route route) => false.

onexf
  • 3,674
  • 3
  • 22
  • 36
Muhammad Amir
  • 2,326
  • 1
  • 12
  • 13
  • 2
    if you want to send params on navigating page then you can use "Navigator.pushNamedAndRemoveUntil(context, "/newRouteName", (r) => false, arguments: { "arg_1": firstArgument, "arg_2": secondArgument });" – Kamlesh May 15 '21 at 08:48
  • This code works for me. – prawito hudoro Jul 02 '22 at 00:44
53

Another alternative is popUntil()

Navigator.of(context).popUntil(ModalRoute.withName('/root'));

This will pop all routes off until you are back at the named route.

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
nleslie
  • 708
  • 5
  • 11
36

Another solution is to use pushAndRemoveUntil(). To remove all other routes use ModalRoute.withName('/')

Navigator.pushAndRemoveUntil(
    context,   
    MaterialPageRoute(builder: (BuildContext context) => Login()), 
    ModalRoute.withName('/')
);

Reference: https://api.flutter.dev/flutter/widgets/NavigatorState/pushAndRemoveUntil.html

Mantawy
  • 361
  • 3
  • 3
  • This is really worked. for my scenario . I have a startup page. Which contain 2 buttons. one is login and the other is signup . once we go inside each of those buttons . inside there is a another route area which can navigate through each page. So if I dont use above code . it will go inside inside and inside like inception film. to prevent this code helps. Thanks for the answer. Awesome. – Learning Curious Dec 18 '22 at 11:03
25

In case you want to go back to the particular screen and you don't use named router can use the next approach

Example:

Navigator.pushAndRemoveUntil(context,
                  MaterialPageRoute(builder: (BuildContext context) => SingleShowPage()),
                  (Route<dynamic> route) => route is HomePage
              );

With route is HomePage you check the name of your widget.

Volodymyr Bilovus
  • 654
  • 2
  • 7
  • 20
19

If you are using namedRoutes,

This statement removes all the routes in the stack and makes the pushed one the root.

then you can do this by simply :

Navigator.pushNamedAndRemoveUntil(context, "/login", (Route<dynamic> route) => false);

Where "/login" is the route you want to push on the route stack.

Kelvin Mboto
  • 361
  • 6
  • 15
17

use popUntil like following

Navigator.popUntil(context, (route) => route.isFirst);

Mohamed Kamel
  • 841
  • 10
  • 15
11

I don't know why no one mentioned the solution using SchedularBindingInstance, A little late to the party though, I think this would be the right way to do it originally answered here

SchedulerBinding.instance.addPostFrameCallback((_) async {
   Navigator.of(context).pushNamedAndRemoveUntil(
      '/login',
      (Route<dynamic> route) => false);
});

The above code removes all the routes and naviagtes to '/login' this also make sures that all the frames are rendered before navigating to new route by scheduling a callback

K.Amanov
  • 1,278
  • 14
  • 23
Mahesh Jamdade
  • 17,235
  • 8
  • 110
  • 131
8

In my case I had this painting Page 1 (Main) -> Page 2 -> Page 3 -> Page 4.

When I had to go to Page 4, the Page 2 and Page 3 going back did not have to appear, but I had to go to Page 1 again. At this point going to Page 4 I did:

Navigator.pushAndRemoveUntil(
              context,
              MaterialPageRoute(
                  builder: (BuildContext context) =>
                      Workout()),
             (Route<dynamic> route) => route.isFirst);

The instructions are: go to page 4 (Workout) and remove all previous pages up to 1, that is (Main).

In your case that can be to switch from anything to a Login, then:

Navigator.pushAndRemoveUntil(
              context,
              MaterialPageRoute(
                  builder: (BuildContext context) =>
                      Login()),
             (Route<dynamic> route) => false);

That is, go to Login and remove all previous pages, because there is a false.

AlexPad
  • 10,364
  • 3
  • 38
  • 48
7

Not sure if I'm doing this right

but this suits my use-case of popping until by root widget

void popUntilRoot({Object result}) {
    if (Navigator.of(context).canPop()) {
      pop();
      popUntilRoot();
    }
}
Vinesh Raju
  • 79
  • 1
  • 2
7

In my case this solution works:

Navigator.pushNamedAndRemoveUntil(" The Route you want to go to " , (Route route) => false);
Mady Daby
  • 1,271
  • 6
  • 18
Mohamed Mansour
  • 197
  • 2
  • 4
  • if you want to go to login or register you can simply call the root :::: Navigator.pushNamedAndRemoveUntil("/ " , (Route route) => false); – Mohamed Mansour May 27 '21 at 16:20
6

This is working for me. Actually, I was working with bloc but my issue was login screen bloc. It was not updating after logout. It was holding the previous model data. Even, I entered the wrong entry It was going to Home Screen.

Step 1:

Navigator.of(context).pushNamedAndRemoveUntil(
        UIData.initialRoute, (Route<dynamic> route) => false);

where, UIData.initialRoute = "/" or "/login"

Step 2:

It's working to refresh the screen. If you are working with Bloc then It will very helpful.

runApp(MyApp());

where, MyApp() is the root class.

Root class (i.e. MyApp) code

class MyApp extends StatelessWidget {

  final materialApp = Provider(
      child: MaterialApp(
          title: UIData.appName,
          theme: ThemeData(accentColor: UIColor().getAppbarColor(),
            fontFamily: UIData.quickFont,
          ),
          debugShowCheckedModeBanner: false,
          //home: SplashScreen(),
          initialRoute: UIData.initialRoute,
          routes: {
            UIData.initialRoute: (context) => SplashScreen(),
            UIData.loginRoute: (context) => LoginScreen(),
            UIData.homeRoute: (context) => HomeScreen(),
          },
          onUnknownRoute: (RouteSettings rs) => new MaterialPageRoute(
              builder: (context) => new NotFoundPage(
                appTitle: UIData.coming_soon,
                icon: FontAwesomeIcons.solidSmile,
                title: UIData.coming_soon,
                message: "Under Development",
                iconColor: Colors.green,
              )
          )));

  @override
  Widget build(BuildContext context) {
    return materialApp;
  }
}

void main() => runApp(MyApp());

Here is My Logout method,

void logout() async {
    SharedPreferences preferences = await SharedPreferences.getInstance();
    preferences.clear();

    // TODO: we can use UIData.loginRoute instead of UIData.initialRoute
    Navigator.of(context).pushNamedAndRemoveUntil(
        UIData.initialRoute, (Route<dynamic> route) => false);
    //TODO: It's working as refresh the screen
    runApp(MyApp());
  }
rockstar
  • 587
  • 4
  • 22
5

Use this, it worked perfectly for me:

 Navigator.pushNamedAndRemoveUntil(
 context, '/loginscreen', (Route<dynamic> route) => false);

Make sure you add the last line, parameter, and you're good to go.

Ephraim
  • 63
  • 1
  • 7
3

First see chrislondon answer, and then know that you can also do this, if you do not have access to the (context).

navigatorKey.currentState.pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);  
Bo Kristensen
  • 1,460
  • 11
  • 8
2
to clear route - 

  onTap: () {
                    //todo to clear route -
                    Navigator.of(context).pop();
                    Navigator.push(context, MaterialPageRoute(builder: (context) => UpdateEmployeeUpdateDateActivity(_token),));
                    widget.listener.onEmployeeDateClick(_day,_month, _year);
}
s.j
  • 621
  • 6
  • 16
0

Newer Flutter version seem to replace

(Route<dynamic> route) => false);

by

ModalRoute.withName('/'));
iKK
  • 6,394
  • 10
  • 58
  • 131