Передача данных между экранами в Flutter
Поскольку я изучаю Флаттер, я пришел к навигации. Я хочу передавать данные между экранами аналогично передаче данных между операциями в Android и передачей данных между контроллерами представления в iOS. Как мне это сделать во Флаттере?
Смежные вопросы:
- Лучший способ передачи данных между виджетами во Flutter
- Флаттер передает данные между виджетами?
- Флаттер / Как передавать и получать данные между Statefulwidget
Я создаю это как канонический Q&A, так как я не мог найти другой для Флаттера. Мой ответ ниже.
13 ответов
Этот ответ будет охватывать как передачу данных вперед, так и передачу данных обратно. В отличие от Android Activity и iOS ViewControllers, разные экраны во Flutter являются просто виджетами. Навигация между ними включает создание чего-либо, называемого маршрутом, и использование Navigator
толкать и вставлять маршруты в стек и из него.
Передача данных вперед на следующий экран
Чтобы отправить данные на следующий экран, вы делаете следующие вещи:
Сделать
SecondScreen
Конструктор принимает параметр для типа данных, которые вы хотите отправить ему. В этом конкретном примере данные определены какString
значение и устанавливается здесь сthis.text
,class SecondScreen extends StatelessWidget { final String text; SecondScreen({Key key, @required this.text}) : super(key: key); ...
Затем используйте
Navigator
вFirstScreen
виджет, чтобы протолкнуть маршрут кSecondScreen
виджет. Вы помещаете данные, которые хотите отправить, в качестве параметра в его конструктор.Navigator.push( context, MaterialPageRoute( builder: (context) => SecondScreen(text: 'Hello',), ));
Полный код для main.dart
это здесь:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter',
home: FirstScreen(),
));
}
class FirstScreen extends StatefulWidget {
@override
_FirstScreenState createState() {
return _FirstScreenState();
}
}
class _FirstScreenState extends State<FirstScreen> {
// this allows us to access the TextField text
TextEditingController textFieldController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First screen')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: TextField(
controller: textFieldController,
style: TextStyle(
fontSize: 24,
color: Colors.black,
),
),
),
RaisedButton(
child: Text(
'Go to second screen',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_sendDataToSecondScreen(context);
},
)
],
),
);
}
// get the text in the TextField and start the Second Screen
void _sendDataToSecondScreen(BuildContext context) {
String textToSend = textFieldController.text;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(text: textToSend,),
));
}
}
class SecondScreen extends StatelessWidget {
final String text;
// receive data from the FirstScreen as a parameter
SecondScreen({Key key, @required this.text}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second screen')),
body: Center(
child: Text(
text,
style: TextStyle(fontSize: 24),
),
),
);
}
}
Передача данных на предыдущий экран
При передаче данных обратно необходимо выполнить следующие действия:
в
FirstScreen
, использоватьNavigator
подтолкнутьSecondScreen
вasync
метод и дождитесь результата, который он вернет после завершения.final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => SecondScreen(), ));
в
SecondScreen
включите данные, которые вы хотите передать в качестве параметра, когда вы открываетеNavigator
,Navigator.pop(context, 'Hello');
Тогда в
FirstScreen
await
закончите, и вы можете использовать результат.setState(() { text = result; });
Вот полный код для main.dart
для вашей справки.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter',
home: FirstScreen(),
));
}
class FirstScreen extends StatefulWidget {
@override
_FirstScreenState createState() {
return _FirstScreenState();
}
}
class _FirstScreenState extends State<FirstScreen> {
String text = 'Text';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: Text(
text,
style: TextStyle(fontSize: 24),
),
),
RaisedButton(
child: Text(
'Go to second screen',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_awaitReturnValueFromSecondScreen(context);
},
)
],
),
),
);
}
void _awaitReturnValueFromSecondScreen(BuildContext context) async {
// start the SecondScreen and wait for it to finish with a result
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(),
));
// after the SecondScreen result comes back update the Text widget with it
setState(() {
text = result;
});
}
}
class SecondScreen extends StatefulWidget {
@override
_SecondScreenState createState() {
return _SecondScreenState();
}
}
class _SecondScreenState extends State<SecondScreen> {
// this allows us to access the TextField text
TextEditingController textFieldController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second screen')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: TextField(
controller: textFieldController,
style: TextStyle(
fontSize: 24,
color: Colors.black,
),
),
),
RaisedButton(
child: Text(
'Send text back',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_sendDataBack(context);
},
)
],
),
);
}
// get the text in the TextField and send it back to the FirstScreen
void _sendDataBack(BuildContext context) {
String textToSendBack = textFieldController.text;
Navigator.pop(context, textToSendBack);
}
}
Это решение очень просто, если передать переменные в конструктор:
первая страница:
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>SecondPage('something')));
вторая страница:
class SecondPage extends StatefulWidget {
String something;
SecondPage(this.something);
@override
State<StatefulWidget> createState() {
return SecondPageState(this.something);
}
}
class SecondPageState extends State<SecondPage> {
String something;
SecondPageState(this.something);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
//now you have passing variable
title: Text(something),
),
...
}
Получите идеальное решение:
С 1-го экрана перейдите к другим как:
Navigator.pushNamed(context, "second",arguments: {"name" : "Bijendra", "rollNo": 65210}); },
На втором экране в методе сборки получить как:
@override Widget build(BuildContext context) { final Map<String, Object>rcvdData = ModalRoute.of(context).settings.arguments; print("rcvd fdata ${rcvdData['name']}"); print("rcvd fdata ${rcvdData}"); return Scaffold(appBar: AppBar(title: Text("Second")), body: Container(child: Column(children: <Widget>[ Text("Second"), ],),),); }
Самый простой способ
FirstPage.dart
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PasswordRoute(usernameController)));
//usernameController - это строковое значение. Если вы хотите передать несколько значений, добавьте все
SecondPage.dart
class PasswordRoute extends StatefulWidget {
final String usernameController;//if you have multiple values add here
PasswordRoute(this.usernameController, {Key key}): super(key: key);//add also..example this.abc,this...
@override
State<StatefulWidget> createState() => _PasswordPageState();
}
class _PasswordPageState extends State<PasswordRoute> {
@override
Widget build(BuildContext context) {
...child: Text(widget.usernameController);
}
}
Приведенные выше ответы полезны для небольшого приложения, но если вы хотите избавиться от головной боли, связанной с постоянным беспокойством о состоянии виджетов, Google представил пакет Provider. https://pub.dev/packages/provider
Взгляните на него или посмотрите эти видео от Андреа Биццотто: https://www.youtube.com/watch?v=MkFjtCov62g // Provider: The Essential Guidehttps://www.youtube.com/watch?v = O71rYKcxUgA& t = 258s // Провайдер: Введение
Узнайте, как пользоваться пакетом Provider, и вы готовы к жизни:)
Первый экран:// отправляем данные на второй экран
Navigator.push(context, MaterialPageRoute(builder: (context) {
return WelcomeUser(usernameController.text);
}));
Второй экран: // получение данных с первого экрана
final String username;
WelcomeUser(this.username);
// используем данные для отображения
body: Container(
child: Center(
child: Text("Welcome "+widget.username,
textAlign: TextAlign.center,
),
),
),
Навигаторы во Flutter похожи на Intent в Android. Мы имеем дело с двумя классами FirstScreen и SecondScreen.
Чтобы передать данные между первым экраном и вторым, выполните следующие действия: Прежде всего добавьте параметр в конструктор класса SecondScreen.
Теперь в классе FirstScreen укажите параметр
Navigator.push(context, MaterialPageRoute(builder: (context)=>SecondScreen(key_name:"Желаемые данные"));
Таким образом, в строке выше "key_name" - это имя параметра, заданного в классе SecondScreen. "Желаемые данные" - это данные, которые должны быть переданы через ключ в класс SecondScreen.
Вот и все, готово!!!
Передача данных на задний экран
Домашняя страница
import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:sqflite_offline/View/Add_data.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { List<Method> items = []; // => List of items that come form next page. @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Hello"), ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.of(context) .push<Method>(MaterialPageRoute(builder: (_) => AddData())) // fetching data form next page. .then((value) => setState(() { if (value?.title_Ctr != "" && value?.desc_Ctr != "") { items.add(Method( title_Ctr: value!.title_Ctr, desc_Ctr: value.desc_Ctr)); } })); }, child: Icon(Icons.add), ), body: items.isNotEmpty ? Column(children: [ Expanded( child: ListView.builder( itemCount: items.length, itemBuilder: ((context, index) { return Container( margin: EdgeInsets.only(top: 10, left: 10, right: 10), padding: EdgeInsets.only(left: 10, right: 10), height: 80, decoration: BoxDecoration( color: Colors.pinkAccent, borderRadius: BorderRadius.circular(10)), child: Center( child: ListTile( title: Text(items[index].title_Ctr), subtitle: Text(items[index].desc_Ctr), leading: Icon(Icons.emoji_people), ), ), ); }))) ]) : Center( child: Text("No Record Found"), )); } }
Добавить страницу со списком
import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; class AddData extends StatefulWidget { const AddData({super.key}); @override State<AddData> createState() => _AddDataState(); } // Creating a Class and constructor. class Method { late String title_Ctr; late String desc_Ctr; Method({required this.title_Ctr, required this.desc_Ctr}); } class _AddDataState extends State<AddData> { // Creating a TextEditingController for two Fiends, //one is for title TextField and second is for Description TextField. TextEditingController titleCtr = TextEditingController(); TextEditingController descCtr = TextEditingController(); // Creating a Method for Passing a data to back page. OnPressed(BuildContext context) { var data = Method(title_Ctr: titleCtr.text, desc_Ctr: descCtr.text); Navigator.pop(context, data); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Add Data")), body: Form(child: Builder(builder: (context) { return Column(children: [ TextFormField( controller: titleCtr, decoration: InputDecoration(hintText: "title"), validator: (value) { var newValue = value ?? ""; if (newValue.isEmpty) { return 'title is Required'; } return null; }, ), TextFormField( controller: descCtr, decoration: InputDecoration(hintText: "Description"), validator: (value) { var newValue = value ?? ""; if (newValue.isEmpty) { return 'Discription is Required'; } return null; }, ), MaterialButton( color: Colors.red, onPressed: () { if (Form.of(context)?.validate() ?? false) { OnPressed(context); } }, child: Text("Save"), ) ]); }))); } }
Скриншот
1) Откуда вы хотите нажать:
onPressed: () async {
await Navigator.pushNamed(context, '/edit',
arguments: userData);
setState(() {
userData = userData;
});}
2) Откуда вы хотите поп:
void updateData() async{
WorldTime instance = locations;
await instance.getData();
Navigator.pop(context, userData);
}
Передача данных на задний экран
Первый экран
final result = await Navigator.of(context).push(MaterialPageRoute(builder: (context)=>const PaymentScreen()));
Второй экран
String selected = "Credit/Debit";
Navigator.pop(context,selected);
Я просто хочу быть здесь, чтобы помочь тому 1%, кто может пройти через то же, что и я, лол.
Не забудьте поставить «ожидание» перед «Navigator.push» на первой странице, иначе никакие данные не будут возвращены на первую страницу при открытии второй страницы...
Вот еще один подход.
В других ответах нет ничего плохого. Я пробовал все упомянутые методы с использованием глобальных виджетов, таких как поставщик, сторонние решения, аргументы навигатора и т. Д. Этот подход отличается тем, что позволяет объединять вызовы и передавать точные данные любого типа, необходимые для виджета, использующего его. Мы также можем получить доступ к событию обработчика завершения и использовать эту технику, не ограничиваясь объектами навигатора.
Вот tldr:
tldr; Мы должны немного перевернуть наше мышление. Данные могут быть переданы в вызываемый виджет, когда вы переходите к нему, используя окончательные аргументы со значениями по умолчанию в целевом виджете. Используя дополнительную функцию, вы можете получить данные из «дочернего» (целевого) виджета.
Полное объяснение можно найти, используя этот SO-ответ., (Суть)
Если вы используете получить пакет, попробуйте это. передача данных с помощью пакета get
проверьте ссылку на получение пакета