在 Flutter 中的屏幕之间传递数据

Passing data between screens in Flutter

在学习Flutter的过程中,我来导航了。我想像 passing data between Activities in Android and passing data between View Controllers in iOS 一样在屏幕之间传递数据。我如何在 Flutter 中做到这一点?

相关问题:

此答案将涵盖向前传递数据和向后传递数据。与 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. 然后使用 FirstScreen 小部件中的 Navigator 将路由推送到 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中,使用Navigatorasync方法中推送(启动)SecondScreen并等待结果完成后它将 return。

    final result = await Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => SecondScreen(),
        ));
    
  2. SecondScreen中,包含弹出Navigator时要作为参数传回的数据。

    Navigator.pop(context, 'Hello');
    
  3. 然后在 FirstScreenawait 将完成,您可以使用结果。

    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);
  }
}

最简单的方法

FirstPage.dart

 Navigator.push(
          context,
          MaterialPageRoute(
              builder: (context) => PasswordRoute(usernameController)));

//usernameController是String值,如果要传多个值就加上all

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);
}
}

通过在构造函数中传递变量,这个解决方案非常简单:

第一页:

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),
   ),
   ...
  }

第一屏: //发送数据到第二屏

 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,
      ),
    ),
  ),

上面的答案对小型应用程序很有用,但如果您想消除持续担心小部件状态的头痛,Google 提供了 Provider 包。 https://pub.dev/packages/provider

看看那个,或观看 Andrea Bizzotto 的这些视频: https://www.youtube.com/watch?v=MkFjtCov62g // 提供商:基本指南 https://www.youtube.com/watch?v=O71rYKcxUgA&t=258s // 提供商:简介

了解如何使用 Provider 包,您将终生受益:)

获得完美解决方案:

  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"),
        ],),),);
    
    }
    

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);
  }

Flutter 中的 Navigators 类似于 Android 中的 Intent。 我们正在处理两个 classes FirstScreen 和 SecondScreen。

为了在第一个屏幕和第二个屏幕之间传递数据,请执行以下操作: 首先在SecondScreenclass构造函数

中添加参数

现在在 FirstScreen class 提供参数

Navigator.push(context, MaterialPageRoute(builder: (context)=>SecondScreen(key_name:"Desired Data"));

所以在上面的行中,“key_name”是 SecondScreen class 中给定的参数名称。 “所需数据”是应该通过密钥传递给 SecondScreen class.

的数据

大功告成!!!

如果您使用 get package 然后试试这个。 passing data with get package

检查获取包package link

这是另一种方法。

其他回答都没有问题。我已经尝试了使用全局小部件(如提供程序、第三方解决方案、导航器参数等)提到的所有方法。这种方法的不同之处在于允许链接调用并将任何类型的精确数据传递给使用它的小部件。我们还可以获得对完成处理程序事件的访问权限,并且可以在不受 Navigator 对象约束的情况下使用此技术。

这里是 tldr:

tldr; We have to turn our thinking on its head a bit. Data can be passed to the called widget when you navigate to it by using final arguments with default values in the destination widget. Using an optional function you can get data back from the 'child' (destination) widget.

完整的解释可以用这个找到 SO answer., (Gist)

我只想在这里帮助那 1% 可能会经历我所做的事情的人哈哈

别忘了在第一页的“Navigator.push”前面加上“await”, 否则,当您从第二页弹出时,第一页将不会返回任何数据...