Как передать нестроковые данные в именованный маршрут во Flutter?
Я использую Flutter для моего мобильного приложения. У меня много экранов, и я использую Навигатор. Я хотел бы использовать "именованные маршруты", но мне также нужно передать нестроковые (например, изображения) моему следующему маршруту.
Я не могу использовать pushNamed()
потому что я не могу передать не строковые данные к нему.
Как я могу использовать именованный маршрут + отправить нестроковые данные?
13 ответов
Именованный маршрут в принципе должен быть самоописуемым. Вы не должны пытаться сделать что-то вроде pushNamed("/myRoute", myObject)
, Вместо этого вы должны сделать что-то вроде pushNamed("myRoute/${myObject.id}")
а затем извлеките этот идентификатор из пути маршрута и используйте его для запроса необходимых данных.
Где onGenerateRoute
доступны как MaterialApp
свойство используется. Например, вы можете сделать что-то вроде этого:
onGenerateRoute: (routeSettings) {
var path = routeSettings.name.split('/');
if (path[0] == "myRoute") {
final foo = path.length > 1 ? int.parse(path[1]) : null;
return new MaterialPageRoute(
builder: (context) => new MyPage(foo: foo),
settings: routeSettings,
);
}
// fallback route here
},
Вы можете использовать параметр routes
вашего приложения для прямой передачи аргументов.
Нравится:
routes: {
HomePage.route: (_) => HomePage(),
DetailsPage.route: (context) =>
DetailsPage(ModalRoute.of(context).settings.arguments),
},
В этом случае полный пример будет выглядеть следующим образом:
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);
}
Используя карты
Нажимая аргументы, вы можете отправить их в виде карты и делать то же самое при их извлечении.
например
При нажатии
Navigator.of(context).pushNamed(
'second',
arguments: {
'title':'This is a String',
or
'Fx': This could be any widget or Function
}
При извлечении аргументов на целевой странице
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
С помощью onGenerateRoute
при передаче маршрута легко передать сложные аргументы Navigator.pushNamed
или же Navigator.pushReplacementNamed
Минимальная установка, чтобы показать концепцию будет
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
в _buildRoute
Метод, мы проверяем имя маршрута и приводим аргументы к требуемому типу.
Недостатком является то, что тип должен быть определен заранее, если требуемый аргумент не является простым типом.
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';
}
}
Просмотры
list.dart
Создайте аргумент маршрута, используя определенный тип, Article
в нашем случае.
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
Определите тип для аргументов
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}'),
],
),
),
),
);
}
}
Здесь много гигантских решений, но вашу проблему можно решить буквально двумя строчками, используя эту библиотеку:https://pub.dev/packages/get
List users = [2,4,6,3]
Get.toNamed('/second', arguments: users);
на втором маршруте
print(Get.arguments);
// out: [2,4,6,3]
Преимущество его использования перед другими решениями заключается в том, что вы можете передавать через аргументы что угодно, даже экземпляры классов, если хотите, и вы можете получать аргументы из любого места, как в вашем пользовательском интерфейсе, так и в вашем блоке, контроллере, вашем провайдере и т. Д. без необходимости передавать аргументы в onGenerateRoute классу.
Поваренная книга Flutter показывает, как перейти на новую страницу и передать на нее нестроковые данные.
Передача данных на следующую страницу
Я начал с Navigator.pushedNamed()
потому что это было просто, и у меня не было данных для передачи. Когда мои потребности изменились, и я хотел передать данные, я переключился на Navigator.push()
,
Пример:
var nextPageData = {foo:'bar'};
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
MyNextPage(myData: nextPageData))
);
Я хотел использовать навигатор именованных маршрутов со значениями, как показано ниже.
Navigator.pushNamed(context, '/increaseBalanceAccountPage',
arguments: {'accountBalanceViewModel': result},);
поэтому я должен определить этот маршрут в виджете materialApp в начале приложения, но я должен указать параметры в экземпляре, чтобы решить свою проблему с некоторой модификацией кода @YuriyLuchaninov, как показано ниже:
MaterialApp(
initialRoute: "/",
routes: {
'/': (context) => SplashScreenPage(),
"/increaseBalanceAccountPage":
(context) =>
UserAccountBalancePage(accountBalanceViewModel: Map<String,Object>
.from(ModalRoute.of(context)!.settings.arguments as Map).values.first as
AccountBalanceViewModel)
},
.....
Я снимаю изображения с помощью камеры, а затем передаю их на страницу подтверждения, например:
ImagePicker.pickImage(source: source).then((File file) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MediaCaptured(file: file),
));
});
Вы можете легко сделать то же самое с любым типом файлов или нестроковых данных.
var foo = "non-string data";
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MediaCaptured(foo: foo),
));
Назовите следующую страницу в маршруте по имени класса, как указано выше.
Просто убедитесь, что ваша новая страница принимает это в своем конструкторе.
// 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;
}
Это довольно просто с этим pakage
ссылка: https://pub.dartlang.org/packages/navigate
Это обеспечивает ваши ожидания и прост в использовании
Navigate.navigate(context,
"home",
transactionType:TransactionType.fromLeft , // optional
replaceRoute: ReplaceRoute.thisOne, //optional
arg: {"transactionType":TransactionType.fromLeft,"replaceRoute":ReplaceRoute.thisOne} //optional
);
Из первого класса StateFul:
Navigator.of(context).pushNamed('/pending_order',arguments: {"staff" : staffObj});
Ко второму классу StateFul:
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(...);}}
Рассмотрим этот тривиальный пример из флаттера. У вас есть класс, созданный следующим образом
class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}
Теперь мы будем передавать объект этого класса в качестве аргумента следующим образом
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: ScreenArguments(
'Extract Arguments Screen',
'This message is extracted in the build method.',
),
);
И тогда вы можете извлечь аргументы следующим образом
final args = ModalRoute.of(context)!.settings.arguments as ScreenArguments;
И это все. Надеюсь, это поможет Источник: передача аргументов именованному маршруту
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]),
);
},
);
}
}