Передача данных между экранами в Flutter

Поскольку я изучаю Флаттер, я пришел к навигации. Я хочу передавать данные между экранами аналогично передаче данных между операциями в Android и передачей данных между контроллерами представления в iOS. Как мне это сделать во Флаттере?

Смежные вопросы:

Я создаю это как канонический Q&A, так как я не мог найти другой для Флаттера. Мой ответ ниже.

13 ответов

Решение

Этот ответ будет охватывать как передачу данных вперед, так и передачу данных обратно. В отличие от Android Activity и iOS ViewControllers, разные экраны во Flutter являются просто виджетами. Навигация между ними включает создание чего-либо, называемого маршрутом, и использование Navigator толкать и вставлять маршруты в стек и из него.

Передача данных вперед на следующий экран

Чтобы отправить данные на следующий экран, вы делаете следующие вещи:

  1. Сделать SecondScreen Конструктор принимает параметр для типа данных, которые вы хотите отправить ему. В этом конкретном примере данные определены как String значение и устанавливается здесь с this.text,

    class SecondScreen extends StatelessWidget {
      final String text;
      SecondScreen({Key key, @required this.text}) : super(key: key);
    
      ...
    
  2. Затем используйте 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),
        ),
      ),
    );
  }
}

Передача данных на предыдущий экран

При передаче данных обратно необходимо выполнить следующие действия:

  1. в FirstScreen, использовать Navigator подтолкнуть SecondScreen в async метод и дождитесь результата, который он вернет после завершения.

    final result = await Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => SecondScreen(),
        ));
    
  2. в SecondScreen включите данные, которые вы хотите передать в качестве параметра, когда вы открываете Navigator,

    Navigator.pop(context, 'Hello');
    
  3. Тогда в 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. С 1-го экрана перейдите к другим как:

    Navigator.pushNamed(context, "second",arguments: {"name" : 
      "Bijendra", "rollNo": 65210});
    },
    
  2. На втором экране в методе сборки получить как:

    @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.

Вот и все, готово!!!

Передача данных на задний экран

  1. Домашняя страница

            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"),
            ));
         }
        } 
    
  2. Добавить страницу со списком

             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"),
        )
      ]);
    })));
       }
       }
    
  3. Скриншот

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

проверьте ссылку на получение пакета

Другие вопросы по тегам