9

I'm trying to pass values between routes using Navigator.pushReplacementNamed(), but I'm not getting it.

I can not figure out what the correct syntax and logic is in which data will exit pushReplacementNamed and get the routes key.

Can you help me?

Enter the code below to facilitate understanding:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(),
      routes: <String, WidgetBuilder> {
        '/form': (BuildContext context) => new FormPage(email: ???, header: {'auth': ???}),
      },
    );
  }
}

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      floatingActionButton: new FloatingActionButton(
        onPressed: () {
          Navigator.pushReplacementNamed(context, '/form', {email: 'myemail@flutter.com', header: {'auth': '1234'}});
        },
        child: new Icon(Icons.navigate_next),
      ),
    );
  }
}

class FormPage extends StatefulWidget {
  FormPage({Key key, this.email, this.header}) : super(key: key);
  String email;
  Map header;
  @override
  FormPageState createState() => new FormPageState();
}

class FormPageState extends State<FormPage> {
  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new Column(
        children: <Widget>[
          new Text(widget.email),
          new Text(widget.header['auth'])
        ],
      ),
    );
  }
}

After correction:

The purpose of trying to use pushReplacementNamed was to remove the route history, so if the user presses the device's back button to get back to the earlier route, the route would no longer exist. Reference

The reason for this implementation is to be used on a login page, if the user has already been authenticated by google sign he is redirected to the next page passing login parameters and could not return to the login page by device's back button.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(),
      routes: <String, WidgetBuilder> {
        '/form': (BuildContext context) => new FormPage(), //new
      },
    );
  }
}

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      floatingActionButton: new FloatingActionButton(
        onPressed: () {
          Navigator.of(context).pushReplacement(                                                         //new
            new MaterialPageRoute(                                                                       //new
              settings: const RouteSettings(name: '/form'),                                              //new
              builder: (context) => new FormPage(email: 'myemail@flutter.com', header: {'auth': '1234'}) //new
            )                                                                                            //new
          );                                                                                             //new
        },
        child: new Icon(Icons.navigate_next),
      ),
    );
  }
}

class FormPage extends StatefulWidget {
  FormPage({Key key, this.email, this.header}) : super(key: key);
  String email;
  Map header;
  @override
  FormPageState createState() => new FormPageState();
}

class FormPageState extends State<FormPage> {
  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new Column(
        children: <Widget>[
          new Text(widget.email),
          new Text(widget.header['auth'])
        ],
      ),
    );
  }
}
rafaelcb21
  • 12,422
  • 28
  • 62
  • 86

2 Answers2

11

I think you might be miss understanding how result works. The result goes back to the previous widget that pushed the last route and is using await for the result of the Future sent back by the previous call to push. Do you simply want to start a route with parameters? Then you can do this.

Navigator.of(context).pushReplacement(
  new MaterialPageRoute(
    settings: const RouteSettings(name: '/form'),
    builder: (context) => new FormPage(
      email: 'myemail@flutter.com',
      header: {'auth': '1234'},
    ),
  ),
);
joe
  • 8,383
  • 13
  • 61
  • 109
Simon
  • 10,932
  • 50
  • 49
8
  1. You can use Navigator.of(context).pushReplacementNamed('/route', arguments: 'Test String'); to pass any object as arguments from first widget.
  2. And use String s = ModalRoute.of(context).settings.arguments; to parse the arguments after initState() method, like didChangeDependencies() or build(BuildContext context) in the second widget.

Eg:

@override
void didChangeDependencies() {
 RouteSettings settings = ModalRoute.of(context)!.settings;
 if (settings.arguments != null) {
  CustomObject obj = settings.arguments as CustomObject;
 }
 super.didChangeDependencies();
}
  1. Instead of Step 2. You can also initialize the widget class with those arguments by using onGenerateRoute: (settings) {} in your App Widget. Eg:

    class App extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
     return MaterialApp(
        /*...*/
         routes: {
           /*Don't add those routes with arguments here..*/
         },
         onGenerateRoute: (settings) {
         if (settings.name == SECOND_SCREEN_ROUTE) {
             if (settings.arguments != null) {
               CustomObject obj = settings.arguments as CustomObject;
               return MaterialPageRoute(
                 builder: (context) {
                   return SecondScreen(obj);
                 },
               );
            }
           }
           assert(false, 'Need to implement ${settings.name}');
           return null;
          });
      }
    }
    
deepak raj
  • 3,331
  • 1
  • 12
  • 20
  • 1
    how can we pass multiple arguments? Can we pass multiple arguments in `Map` format? Kindly suggest. Thanks. – Kamlesh Jul 12 '21 at 16:28
  • 1
    I am passing multiple parameters in `Map` format instead of `String` because I have to pass multiple params. But getting error like `type 'Null' is not a subtype of type 'Map' in type cast`. Any suggestion? – Kamlesh Jul 12 '21 at 16:38
  • @Kamlesh make sure you are parsing arguments in didChangeDependencies() or build(BuildContext context) – deepak raj Jul 13 '21 at 07:13
  • Kindly update your answer and pass 2 params and also show how can we get/use params in `didChangeDependencies()`. Look forward to hear you. Thanks. – Kamlesh Jul 27 '21 at 10:18
  • @Kamlesh updated my answer with some examples. Please check it. – deepak raj Jul 27 '21 at 17:52
  • Thank you so much for sharing your knowledge. I have passed 2 parameters at the time of pushreplacementnamed navigation. I can get them `settings.arguments` in `onGenerateRoute:`. I am passing those 2 params in `Map` type on `SecondScreen`. I can use them as widget.param1, widget.param2 but when I use `ModalRoute.of(context)!.settings` in `didChangeDependencies()`, it does not work for me and giving me error like Null cannot be Map. Could you please sugggest me what is the issue and why am I not able to use `ModalRoute.of(context)!.settings` in `didChangeDependencies()`? – Kamlesh Jul 27 '21 at 18:08