Syph
Syph

Reputation: 1319

Navigator pass arguments with pushNamed

Might have been asked before but I can't find it but how do you pass a arguments to a named route?

This is how I build my routes

Widget build(BuildContext context) {
    return new Navigator(
      initialRoute: 'home/chooseroom',
      onGenerateRoute: (RouteSettings settings) {
        WidgetBuilder builder;
        switch (settings.name) {
          case 'home/chooseroom':
            // navigates to 'signup/choose_credentials'.
            builder = (BuildContext _) => new ChoosePage();
            break;
          case 'home/createpage':
            builder = (BuildContext _) => new CreateRoomPage();
            break;
          case 'home/presentation':
            builder = (BuildContext _) => new Presentation();
            break;
          default:
            throw new Exception('Invalid route: ${settings.name}');
        }
        return new MaterialPageRoute(builder: builder, settings: settings);
      },
    );

This is how you call it Navigator.of(context).pushNamed('home/presentation')

But what if my widget is new Presentation(arg1, arg2, arg3)?

Upvotes: 78

Views: 120322

Answers (16)

Anang Hajiana
Anang Hajiana

Reputation: 21

Screen 1 :

Navigator.push(
context,
MaterialPageRoute(
    settings: RouteSettings(arguments: e),
    builder: (context) =>
    const AnnouncementDetail()),
);

screen 2 :

//get parameters
final arg = ModalRoute.of(context)!.settings.arguments as Announcement;

Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text('Anang', style: TextStyle(fontWeight: FontWeight.bold),),
            Text('Diposting pada ${DateFormat("d MMMM yyyy", "id_ID")
                .format(DateTime.parse('${arg.announcementDate!} 00:00:00.000'))}', style: const TextStyle(fontWeight: FontWeight.normal)),
            const SizedBox(height: 16),
            Text(arg.announcementTitle!, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 20),),
            const SizedBox(height: 16),
            Html(
                style : {
                  "body": Style(
                    padding: EdgeInsets.zero,
                    margin: Margins(
                      bottom: Margin.zero(),
                      left: Margin.zero(),
                      top: Margin.zero(),
                      right: Margin.zero(),
                    )
                  )
                },
                data: arg.announcement)
          ],
        )

Announcement.dart

class Announcement {
  Announcement({
      this.id, 
      this.announcementDate, 
      this.announcementTitle, 
      this.announcement, 
      this.month, 
      this.createdBy, 
      this.createdAt, 
      this.updatedAt,});

  Announcement.fromJson(dynamic json) {
    id = json['id'];
    announcementDate = json['announcement_date'];
    announcementTitle = json['announcement_title'];
    announcement = json['announcement'];
    month = json['month'];
    createdBy = json['created_by'];
    createdAt = json['created_at'];
    updatedAt = json['updated_at'];
  }
  int? id;
  String? announcementDate;
  String? announcementTitle;
  String? announcement;
  String? month;
  int? createdBy;
  String? createdAt;
  String? updatedAt;

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['id'] = id;
    map['announcement_date'] = announcementDate;
    map['announcement_title'] = announcementTitle;
    map['announcement'] = announcement;
    map['month'] = month;
    map['created_by'] = createdBy;
    map['created_at'] = createdAt;
    map['updated_at'] = updatedAt;
    return map;
  }

}

Upvotes: 0

Sadikul Haque Sadi
Sadikul Haque Sadi

Reputation: 599

That's how to send parameters / arguments-

ElevatedButton(
                      onPressed: () {
                        Navigator.push(
                            context,
                            MaterialPageRoute(
                                builder: (context) => NewPresentation(
                                      id: '101',
                                      name: 'Unnamed',
                                    )));
                      },
                      child: const Text('Submit'))

And that's how to receive -

class NewPresentation extends StatefulWidget {
  final String id, name;
  
  const NewPresentation({Key? key, required this.id, required this.name}) : super(key: key);
  @override
  State<NewPresentation> createState() => _NewPresentationState();
}

class _NewPresentationState extends State<NewPresentation> {
  Widget build(BuildContext context) {
    return Text(widget.id + widget.name);
  }
}

Upvotes: 0

Elio Muculo
Elio Muculo

Reputation: 81

After a long day fighting with the routes and onGeneratedRoutes on how i should pass the data and retrieve i finally understand it here is what i learned about

routes: - the property of MaterialApp

routes: {'/profile': (context) => const Profile()}

onTap () {
    Navigator.pushNamed(context, '/profile', arguments: {'name': 'someone'});
};

On the profile Screen

  final args = ModalRoute.of(context)!.settings.arguments as Map;

don't forget do Downcast to the right data type using the as keyword.

useGeneratedRoute: - the property of MaterialApp for dynamic routes

onGenerateRoute: Routes.RouteGenerator,

on Routes class

static Route<dynamic> generateR(RouteSettings settings) {
    
    switch (settings.name) {
        final args = settings.arguments;
        case 'profile':
          if (args is Map) {
              return MaterialPageRoute(
                  builder: (context) => Profile(
                  title: args,
              ));
          }
    default:
      return MaterialPageRoute(
          builder: ((context) => const NotFound()));
  }

}

on class Profile create a constructor and a field of type Map or whatever you what to pass as data.

 final Map<String> data;

 const Profile({
     super.key,
     required this.data,
  });

and you can now use the data as you wish.

child: const Text(data[name]);

So use the best option for your context i struggle so much to understand because i was get the null error. hope it's help you need

Upvotes: 0

madlick71
madlick71

Reputation: 2436

No need for onGenerateRoute. Simply use

var exampleArgument = 'example string';

Navigator.pushNamed(
    context,
    '/otherscreen',
    arguments: {'exampleArgument': exampleArgument},
);

and extract the arguments as follows:

@override
Widget build(BuildContext context) {
    final arguments = (ModalRoute.of(context)?.settings.arguments ?? <String, dynamic>{}) as Map;

    print(arguments['exampleArgument']);

    return Scaffold(...);
}

Upvotes: 147

Spsnamta
Spsnamta

Reputation: 509

screeen_1.dart

Navigator.of(context).pushNamed('/screen2',arguments: {'var1': val1 ,'var2': val2, "var3": val3 ,"var4" : val4}); //sending of the values to route_generator.dart

route_generator.dart

class RouteGenerator {
  static Route<dynamic> generateRoute(RouteSettings settings) //from screeen_1.dart
{
    // Getting arguments passed in while calling Navigator.pushNamed
    Map args = settings.arguments;

switch (settings.name) {
case '/screen2':
        if (args is Map) {
          return MaterialPageRoute(
            builder: (_) => screen_2(args),
          );
        }
        return _errorRoute(); //optional written in a scaffold when any error arrises
default:
return _errorRoute(); //optional written in a scaffold when any error arrises

}

screen_2.dart

screen_2(Map args) //follow the params from the route_generator.dart
{
  return Scaffold(
    body: FutureBuilder(
        future: _screen_2EndpointProvider.fetchScreen_2(args['var1'], args["var2"], args['var3'], args['var4']),  //API call inside which it called the args 
        builder: (context, snapshot) {
          return snapshot.hasData
              ? screen_2(param: snapshot.data)
              : Center(child: CircularProgressIndicator());
        }),
  );
}

I tried to provide a simple example, if it could be helpful to anyone, because i was facing the same error after a lots of trials it got solved.

Upvotes: 0

Michael K Madison
Michael K Madison

Reputation: 2209

It took me a while to notice this, as I'm a newbie to Flutter. But the arguments you add using Navigator.pushNamed get sent directly to the widget you pushed NOT the MaterialApp for routing.

So in widget you push a new screen from you'll have:

Navigator.pushNamed(
   context,
   SomePage.routeName,
   arguments: {
      'v1': 'data1',
      'v2': 'data2',
      'v3': 'data3',
   },
)

You won't need those arguments in your constructor at all. Instead your pull them out in the SomePage widget like the others are saying; namely via:

final arg = ModalRoute.of(context)!.settings.arguments as Map;

and can assign them within SomePage build like:

randomVar1 = arg['v1'];
randomVar2 = arg['v2'];
randomVar3 = arg['v3'];

using whatever keys you put in.

if you want MaterialApp to handle it then you use the onGenerateRoute method. This took me forever to notice that the arguments go directly to the widget pushed. For me it was counter-intuitive.

Upvotes: 14

Michael K Madison
Michael K Madison

Reputation: 2209

It took me a while to notice this, as I'm a newbie to Flutter. But the arguments you add using Navigator.pushNamed get sent directly to the widget you pushed NOT the MaterialApp for routing.

You don't need those arguments in your constructor at all. Instead your pull them out in the widget like the others are saying; namely via:

final arg = ModalRoute.of(context)!.settings.arguments as Map;

and can assign them like:

randomVar1 = arg['v1'];
randomVar2 = arg['v2'];
randomVar3 = arg['v3'];

using whatever keys you put in.

if you want MaterialApp to handle it then you use the onGenerateRoute method. This took me forever to notice that the arguments go directly to the widget pushed. For me it was counter-intuitive.

Upvotes: 0

sharma_kunal
sharma_kunal

Reputation: 2192

// Main Screen from where we want to Navigate

Navigator.pushNamed(
                              context,
                             "/ScreenName",
                              arguments: {
                                'id': "20" 
                              });

I also faced the same issue I was using NavigationRoutes

class NavigationRoutes {
  static Route<dynamic> generateScreenRoute(RouteSettings settings) {
   // TODO below we are able to get Arguments list
    Map args = settings.arguments;
    
    switch (settings.name) {
      
      case "Screen Name": 
        return MaterialPageRoute(builder: (context) => ScreenName(args)); 
      // TODO pass above argument here and get in the form of Constructor in Screen 
      //   Name Class
    }

}

Upvotes: -3

Jithin Jude
Jithin Jude

Reputation: 920

Pass arguments:

Navigator.pushNamed(YourScreen.routeName, arguments: {"title":myTitle, "user_name":userName});

Extract arguments:

        Map<String, dynamic> arguments = new Map<String, dynamic>.from(settings.arguments);
        page = MyRecordingScreen(title: arguments["title"], tags: arguments["user_name"], );

Upvotes: 3

Payam Khaninejad
Payam Khaninejad

Reputation: 7996

For named Routes with multiple arguments or dynamic object you need to do as follow(this is MVVM pattern example):

navigator:

void navigateEditParty(int index) {
    _navigationService.navigateTo(PartyEditRoute,
        arguments: {"hunter": hunter, "index": index});
  }

router:

case PartyEditRoute:
  Map args = settings.arguments;
  return _getPageRoute(
    routeName: settings.name,
    viewToShow: PartyEditView(
      hunter: args["hunter"],
      index: args["index"],
    ),
  );

class:

PartyEditView({Key key, this.hunter, this.index}) : super(key: key);

Upvotes: 1

Baker
Baker

Reputation: 28060

Note: new routing options may be available soon.


Could this be a possible option?

Use push with a RouteSettings argument specifying the named route.

This way you can directly pass arguments of any type (including objects) to your destination Widget in a type safe manner and skip using arguments. You won't need to create a single-use throwaway arguments class nor a Map.

RouteSettings in push can supply a named Route to your Navigation stack which you can search for / use in future routing decisions, just the same as if you had used pushNamed.

Push + RouteSettings

To use push with a named route, use RouteSettings argument with the route name.

Example: a user logs in on Page1 and now you want push them from Page1 to Page2

Directly inside Page1 pass the User object (loggedInUser) to Page2 within a Navigator.push call and use a RouteSettings arg with your route name (/page2).

Navigator.push(context, MaterialPageRoute(
  builder: (context) => Page2(user: loggedInUser),
  settings: RouteSettings(name: '/page2')
));

And in Page2 widget, you can expect and use the User object directly.

class Page2 extends StatefulWidget {
  final User loggedInUser;

  Page2(this.loggedInUser);

  @override
  _Page2State createState() => _Page2State();
}

class _Page2State extends State<Page2> {
  User loggedInUser;
  @override
  void initState() {
    super.initState();
    loggedInUser = widget.loggedInUser;
    print(loggedInUser.name);
  }
  
  @override
  Widget build(BuildContext context) {
  }
}

Later on you can use /page2 route name. For example if you're at /page3 and you want to popUntil(context, ModalRoute.withName('/page2')), this allows that.

Under the Hood

Flutter's Navigator class shows pushNamed uses push + routeNamed and routeNamed uses RouteSettings.

Future<T> pushNamed<T extends Object>(
    String routeName, {
    Object arguments,
  }) {
    return push<T>(_routeNamed<T>(routeName, arguments: arguments));
  }
Route<T> _routeNamed<T>(String name, { @required Object arguments, bool allowNull = false }) {
final RouteSettings settings = RouteSettings(
      name: name,
      arguments: arguments,
    );
    Route<T> route = widget.onGenerateRoute(settings) as Route<T>;
return route;

Upvotes: 2

Konrad Grzyb
Konrad Grzyb

Reputation: 2035

For simple navigation with arguments from WidgetA to WidgetB

  1. Define routes in MaterialApp main widget:
    return MaterialApp(
       routes: {
           '/routeAB': (context) => WidgetB(),
       },
  1. In WidgetA use pushNamed method to navigate to WidgetB:
  onTap: () { 
     Navigator.pushNamed(context, '/routeAB', 
       arguments: {
          'arg1': val1,
          'arg2': val2,
          ...
  1. Get arguments in WidgetB:
    Map args = ModalRoute.of(context).settings.arguments;

Upvotes: 6

junnyea
junnyea

Reputation: 640

It is always best to wrap your arguments in an object to avoid error prone code.

Below are the working example on how you can achieve it in flutter dart.

To send

Navigator.of(context).pushNamed(Routes.submitSuccess, arguments: successModel);

To Receive

  case Routes.submitSuccess:
    if (settings.arguments is SubmitSuccessModel) {//Check for instance here
      return CupertinoPageRoute(
          builder: (_) =>
              SubmitSuccessPage(successModel: settings.arguments));
    }

Model Object

class SubmitSuccessModel {
  SubmitSuccessModel(
      {this.title, this.desc, this.actionButtonName, this.widget});
  String title;
  String desc;
  String actionButtonName;
  Widget widget;
}

final SubmitSuccessModel successModel = SubmitSuccessModel(
  title: 'Title',
  desc: 'Desc',
  actionButtonName: 'Done',
);

Upvotes: 0

Micael Nascimento
Micael Nascimento

Reputation: 91

Arguments can be any object, you can make an array as you can see:

Navigator.of(context).pushNamed('/upload', arguments: {'_imagePath':_imagePath,
      'num_docfiscal':num_docfiscal,'dta_docfiscal':dta_docfiscal});

and access to the router class.

Upvotes: 9

wiradikusuma
wiradikusuma

Reputation: 1919

pushNamed() now supports arguments as of this merged pull request. If you can't wait, switch to channel master (flutter channel master and probably followed by flutter upgrade).

How to send:

    Navigator.pushNamed(ctx, '/foo', arguments: someObject);

How to receive:

...
    return MaterialApp(
        ...
        onGenerateRoute: _getRoute,
        ...
    );
...

Route<dynamic> _getRoute(RouteSettings settings) {
    if (settings.name == '/foo') {
        // FooRoute constructor expects SomeObject
        return _buildRoute(settings, new FooRoute(settings.arguments));
    }

    return null;
}

MaterialPageRoute _buildRoute(RouteSettings settings, Widget builder) {
    return new MaterialPageRoute(
        settings: settings,
        builder: (ctx) => builder,
    );
}

The "arguments" can be any object, e.g. a map.

Upvotes: 46

Buffer Underflow
Buffer Underflow

Reputation: 646

Basically you have 2 options :

  • Use some 3rd party package for routing - I think the best is Fluro .
  • exploit onGenerateRoute . This option is limited to args you can pass (string/numbers)

To use second option, assuming you want to pass three arguments: Navigator.of(context).pushNamed('home/presentation:arg1:1337:hello')

MaterialApp ( 
         ... ,
         onGenerateRoute: handleRoute,
         routes:... , ) 

Route<dynamic> handleRoute(RouteSettings settings) {

    WidgetBuilder builder;

    final List<String> uri = settings.name.split('/');

    if (uri[0].startsWith('home')) {

      // handle all home routes:
      if(uri[1].startsWith('presentation:'){
         // cut slice by slice
         final String allArgs = 
               uri[1].substring('presentation:'.length);
         final List<String> args = allArgs.split(':');

          // use your string args
         print(args[0]);             // prints "arg1"
         int x = int.parse(args[1]); // becomes 1337
         print(args[2]);             // prints "hello"

         builder = (ctx)=> Presentation(args[0],args[1],args[2]);
...

Upvotes: 7

Related Questions