How do I pass non-string data to a named route in Flutter?

DartFlutter

Dart Problem Overview


I have many screens, and I'm using the Navigator. I'd like to use "named routes", but I also need to pass non-string (such as images) to my next route.

I can't use pushNamed() because I can't pass non-string data to it.

How can I use a named route + send non-string data?

Dart Solutions


Solution 1 - Dart

EDIT:

It is now possible to pass complex arguments to Navigator.pushNamed:

String id;
Navigator.pushNamed(context, '/users', arguments: id);

It can then be used within onGenerateRoute to customize route building with these arguments:

MaterialApp(
  title: 'Flutter Hooks Gallery',
  onGenerateRoute: (settings) {
    final arguments = settings.arguments;
    switch (settings.name) {
      case '/users':
        if (arguments is String) {
          // the details page for one specific user
          return UserDetails(arguments);
        }
        else {
          // a route showing the list of all users
          return UserList();
        }
      default:
        return null;
    }
  },
);

Solution 2 - Dart

You can use the parameter routes of your App for directly passing arguments.

Like this:

  routes: {
    HomePage.route: (_) => HomePage(),
    DetailsPage.route: (context) =>
        DetailsPage(ModalRoute.of(context).settings.arguments),
  },

In this case, the complete example will look like the next:

import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          initialRoute: HomePage.route,
          routes: {
            HomePage.route: (_) => HomePage(),
            DetailsPage.route: (context) =>
                DetailsPage(ModalRoute.of(context).settings.arguments),
          },
        );
      }
    }
    
    class HomePage extends StatelessWidget {
      static const String route = '/';
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Navigator.pushNamed(context, '/details',
                  arguments: ScreenArguments(
                    'My Details',
                    'Some Message',
                  ));
            },
          ),
        );
      }
    }
    
    class DetailsPage extends StatelessWidget {
      static const String route = '/details';
    
      final ScreenArguments arguments;
    
      DetailsPage(this.arguments);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(arguments.title),
          ),
          body: Center(
            child: Text(arguments.message),
          ),
        );
      }
    }
    
    class ScreenArguments {
      final String title;
      final String message;
    
      ScreenArguments(this.title, this.message);
    }

Solution 3 - Dart

By Using Maps

While pushing the arguments u can push that in map form and can do the same while extracting them.

e.g.

While Pushing

Navigator.of(context).pushNamed(
              'second',
              arguments: {
                'title':'This is a String',
                       or
                 'Fx': This could be any widget or Function
              }

While Extracting the arguments in the target page

final routes=ModalRoute.of(context).settings.arguments as Map<String,String>;

    return Scaffold(
      appBar: AppBar(
              title: Text(routes['title']),
              ),
      body: Container(
        child: Center(
          child: RaisedButton(
            child: Text("Back"),
            onPressed: ()=>Navigator.of(context).pop(),
          ),
        ),
      ),
    );

and choose your map accordingly accordingly

Solution 4 - Dart

UPDATE: 3rd April, 2021

This answer is old and Flutter navigation has evolved considerably since then. This may not be the best way to handle navigation with current versions, please consider other answers. I will leave this here for historical purposes.


Using onGenerateRoute it is easy to pass complex arguments on route transition with Navigator.pushNamed or Navigator.pushReplacementNamed

A minimal setup to show the concept would be

main.dart

import 'package:flutter/material.dart';
import 'package:navigator/routes.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Navigation Demo',
      theme: ThemeData(
        primarySwatch: Colors.teal,
      ),
      onGenerateRoute: (RouteSettings settings) {
        return MaterialPageRoute(
          builder: (BuildContext context) => makeRoute(
                context: context,
                routeName: settings.name,
                arguments: settings.arguments,
              ),
          maintainState: true,
          fullscreenDialog: false,
        );
      },
    );
  }
}

routes.dart

In the _buildRoute method we check the route name and cast arguments to a required type.

A draw back is that the type has to be defined before hand if required argument is not a simple type.

import 'package:flutter/material.dart';

import 'package:navigator/list.dart';
import 'package:navigator/details.dart';

Widget makeRoute(
    {@required BuildContext context,
    @required String routeName,
    Object arguments}) {
  final Widget child =
      _buildRoute(context: context, routeName: routeName, arguments: arguments);
  return child;
}

Widget _buildRoute({
  @required BuildContext context,
  @required String routeName,
  Object arguments,
}) {
  switch (routeName) {
    case '/':
      return ArticleList();
    case '/ArticleView':
      Article article = arguments as Article;
      return ArticleView(article: article);
    default:
      throw 'Route $routeName is not defined';
  }
}

Views

list.dart

Construct the route argument using a defined type, Article in our case.

import 'package:flutter/material.dart';
import 'package:navigator/details.dart' show Article;

class ArticleList extends StatefulWidget {
  @override
  _ArticleListState createState() => _ArticleListState();
}

class _ArticleListState extends State<ArticleList> {
  List<Article> articles = [
    Article(
        id: 1,
        title: 'Article 1',
        author_name: 'Nilotpal',
        summary: 'Article 1 summary'),
    Article(
        id: 2,
        title: 'Article 2',
        author_name: 'Mike',
        summary: 'Article 2 summary'),
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Articles'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            ListTile(
              title: Text('${articles[0].title}'),
              subtitle: Text('by ${articles[0].author_name}'),
              onTap: () {
                Navigator.of(context)
                    .pushNamed('/ArticleView', arguments: articles[0]);
              },
            ),
            ListTile(
              title: Text('${articles[1].title}'),
              subtitle: Text('by ${articles[1].author_name}'),
              onTap: () {
                Navigator.of(context)
                    .pushNamed('/ArticleView', arguments: articles[1]);
              },
            ),
          ],
        ),
      ),
    );
  }
}

details.dart

Define a type for the arguments

import 'package:flutter/material.dart';

class Article {
  final int id;
  final String author_name;
  final String title;
  final String summary;

  Article(
      {@required this.id,
      @required this.author_name,
      @required this.title,
      @required this.summary});
}

class ArticleView extends StatelessWidget {
  final Article _article;

  ArticleView({@required Article article}) : _article = article;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('${_article.title}'),
      ),
      body: SafeArea(
        top: true,
        child: Center(
          child: Column(
            children: <Widget>[
              Text('${_article.author_name}'),
              Text('${_article.summary}'),
            ],
          ),
        ),
      ),
    );
  }
}

Solution 5 - Dart

The Flutter Cookbook shows how to navigate to a new page and pass non-string data to it.

Passing data to next page

I started with Navigator.pushedNamed() because it was simple and I didn't have any data to pass. When my needs changed and I wanted to pass data, I switched to Navigator.push().

Example:

var nextPageData = {foo:'bar'};

Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => 
     MyNextPage(myData: nextPageData))
 );

Solution 6 - Dart

I am capturing images with camera then passing them through to a confirmation page like so:

   ImagePicker.pickImage(source: source).then((File file) {
    Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => MediaCaptured(file: file),
        ));
  });

You could easily do the same with any type of file or non-string data.

var foo = "non-string data";
Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => MediaCaptured(foo: foo),
        ));

Call the next page in the route by it's class name, as above.

Just make sure your new page accepts this in it's constructor.

 // Stateful Widget
class MediaCaptured extends StatefulWidget {
    MediaCaptured({ Key key, @required this.foo,}) : super(key: key);
    final var foo;
}

// StatelessWidget
class MediaCaptured extends StatelessWidget {
    MediaCaptured(this.foo);
    var foo;
}

Solution 7 - Dart

For the outcome of this problem, I developed the package

link: https://pub.dartlang.org/packages/navigate

That provide to much your expect and easy to use

Navigate.navigate(context,
                      "home",
                      transactionType:TransactionType.fromLeft , // optional
                      replaceRoute: ReplaceRoute.thisOne, //optional
                      arg: {"transactionType":TransactionType.fromLeft,"replaceRoute":ReplaceRoute.thisOne} //optional
                      );

Solution 8 - Dart

From First StateFul class :

Navigator.of(context).pushNamed('/pending_order',arguments: {"staff" : staffObj});

To Second StateFul class :

  class PendingOrders extends StatefulWidget {
  @override
  _PendingOrdersState createState() => _PendingOrdersState();
}

class _PendingOrdersState extends State<PendingOrders> {
  StaffModel staffModelObj;

  @override
  Widget build(BuildContext context) {
    final routes =
        ModalRoute.of(context).settings.arguments as Map<String, dynamic>;
    if (routes != null) {
      staffModelObj = routes["staff"];
    }
    return Scaffold(...);}}

Solution 9 - Dart

Consider this trivial example from flutter. You have a class created as follows

class ScreenArguments {
   final String title;
   final String message;

  ScreenArguments(this.title, this.message);
}

Now we will pass an object of this class as an argument as follows

Navigator.pushNamed(
  context,
  ExtractArgumentsScreen.routeName,
  arguments: ScreenArguments(
    'Extract Arguments Screen',
    'This message is extracted in the build method.',
  ),
);

And then you can extract the arguments as follows

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

And that is all. Hope this helps Source: passing arguments to a named route

Solution 10 - Dart

We can pass any type of arguments when declaring routes as constructor arguments as below,

For example to send a list of Strings,

List<String> titles = [];

void main() => runApp(
      new MaterialApp(
        home: new FirstPage(),
        routes: <String, WidgetBuilder>{
          "/SecondPage": (BuildContext context) => new SecondPage(titles),
        },
      ),
    );

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new RaisedButton(onPressed: () {
        Navigator.of(context).pushNamed('/SecondPage');
      }),
    );
  }
}

class SecondPage extends StatelessWidget {
  final List<String> titles;

  SecondPage(this.titles);

  @override
  Widget build(BuildContext context) {
    return new ListView.builder(
      itemBuilder: (context, index) {
        return new ListTile(
          title: new Text(titles[index]),
        );
      },
    );
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSeth LaddView Question on Stackoverflow
Solution 1 - DartRémi RousseletView Answer on Stackoverflow
Solution 2 - DartYuriy LuchaninovView Answer on Stackoverflow
Solution 3 - Dartdaddy_View Answer on Stackoverflow
Solution 4 - DartnilobarpView Answer on Stackoverflow
Solution 5 - DartdevdankeView Answer on Stackoverflow
Solution 6 - DartSeanThomasView Answer on Stackoverflow
Solution 7 - DartRavindra BhanderiView Answer on Stackoverflow
Solution 8 - DartAnil KumarView Answer on Stackoverflow
Solution 9 - DartUjah JideoforView Answer on Stackoverflow
Solution 10 - DartVinoth KumarView Answer on Stackoverflow