J'ai plusieurs écrans et j'utilise la variable Navigator
. J'aimerais utiliser "itinéraires nommés", mais je dois également passer des chaînes (telles que des images) à mon prochain itinéraire.
Je ne peux pas utiliser pushNamed()
car je ne peux pas lui transmettre de données autres que des chaînes.
Comment puis-je utiliser un itinéraire nommé + envoyer des données autres que des chaînes?
MODIFIER:
Il est maintenant possible de passer des arguments complexes à Navigator.pushNamed
:
String id;
Navigator.pushNamed(context, '/users', arguments: id);
Il peut ensuite être utilisé dans onGenerateRoute
pour personnaliser la construction de route avec ces 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;
}
},
);
Avec onGenerateRoute
, il est facile de passer des arguments complexes lors de la transition d’itinéraire avec Navigator.pushNamed
ou Navigator.pushReplacementNamed
Une configuration minimale pour montrer le concept serait
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,
);
},
);
}
}
Dans la méthode _buildRoute
, nous vérifions le nom de la route et convertissons les arguments en un type requis.
Un inconvénient est que le type doit être défini à l'avance si l'argument requis n'est pas un type simple.
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';
}
}
Des vues
Construisez l'argument de route en utilisant un type défini, Article
dans notre cas.
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]);
},
),
],
),
),
);
}
}
Définir un type pour les 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}'),
],
),
),
),
);
}
}
Vous pouvez utiliser le paramètre routes
de votre application pour passer directement des arguments.
Comme ça:
routes: {
HomePage.route: (_) => HomePage(),
DetailsPage.route: (context) =>
DetailsPage(ModalRoute.of(context).settings.arguments),
},
Dans ce cas, l'exemple complet ressemblera au suivant:
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);
}
Pour résoudre ce problème, j'ai développé le package
lien: https://pub.dartlang.org/packages/navigate
Que fournir à votre guise et facile à utiliser
Navigate.navigate(context,
"home",
transactionType:TransactionType.fromLeft , // optional
replaceRoute: ReplaceRoute.thisOne, //optional
arg: {"transactionType":TransactionType.fromLeft,"replaceRoute":ReplaceRoute.thisOne} //optional
);
Je capture des images avec l'appareil photo, puis je les passe sur une page de confirmation comme ceci:
ImagePicker.pickImage(source: source).then((File file) {
Navigator.Push(
context,
MaterialPageRoute(
builder: (context) => MediaCaptured(file: file),
));
});
Vous pouvez facilement faire la même chose avec n'importe quel type de fichier ou de données non-chaîne.
var foo = "non-string data";
Navigator.Push(
context,
MaterialPageRoute(
builder: (context) => MediaCaptured(foo: foo),
));
Appelez la page suivante de l'itinéraire par son nom de classe, comme ci-dessus.
Assurez-vous simplement que votre nouvelle page accepte cela dans son constructeur.
// 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;
}
Le livre de recettes Flutter montre comment naviguer vers une nouvelle page et lui transmettre des données non chaîne.
J'ai commencé avec Navigator.pushedNamed()
parce que c'était simple et que je n'avais aucune donnée à transmettre. Lorsque mes besoins ont changé et que je souhaitais transmettre des données, je suis passé à Navigator.Push()
.
Exemple:
var nextPageData = {foo:'bar'};
Navigator.Push(
context,
MaterialPageRoute(builder: (context) =>
MyNextPage(myData: nextPageData))
);