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

Posted

技术标签:

【中文标题】在 Flutter 中的屏幕之间传递数据【英文标题】:Passing data between screens in Flutter 【发布时间】:2019-05-20 12:57:28 【问题描述】:

在学习 Flutter 时,我开始学习导航。我想在屏幕之间传递数据,类似于passing data between Activities in android 和passing data between View Controllers in ios。如何在 Flutter 中做到这一点?

相关问题:

The best way to passing data between widgets in Flutter Flutter pass data between widgets? Flutter/ How to pass and get data between Statefulwidget

【问题讨论】:

你可以使用 Flutter 中实现Redux 框架的库。例如,flutter_flux 实现了由ActionsStoresStoreWatchers (pub.dartlang.org/packages/flutter_flux) 组成的单向数据流模式。提供了一个很好的框架来控制应用程序状态并传递数据 最后一个链接其实是***.com/questions/46057353/…的副本 第二个链接实际上也是如此 TODO 为提供者包添加答案 遗憾的是,在 Navigator.push() 之后使用 StreamProvider 和 Provider.of() 时,没有一个答案对实时流有帮助,它不再是实时的了。 【参考方案1】:

我只是想在这里帮助那 1% 的人,他们可能会经历我所做的事情 Lol

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

【讨论】:

【参考方案2】:

这是另一种方法。

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

这是 tldr:

tldr;我们必须稍微改变一下思路。数据可以 当您使用 final 导航到它时传递给被调用的小部件 目标小部件中具有默认值的参数。使用 可选功能,您可以从“孩子”(目的地)取回数据 小部件。

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

【讨论】:

【参考方案3】:

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

首页:

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

【讨论】:

这里不需要传递数据给statereturn SecondPageState(this.something);你可以直接以widget.something访问SecondPageState中的数据而不需要传递给state;【参考方案4】:

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

查看获取包package link

【讨论】:

【参考方案5】:

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

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

现在在 FirstScreen 类中提供参数

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

所以在上一行中,“key_name”是 SecondScreen 类中给出的参数的名称。 "Desired Data" 是数据应该通过密钥传递给 SecondScreen 类。

大功告成!!!

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

获得完美的解决方案:

    从第一个屏幕导航到其他屏幕:

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

【讨论】:

嗨,无论如何,在加载第二个小部件后重置参数。谢谢【参考方案8】:

上面的答案对于小型应用程序很有用,但如果您想消除不断担心小部件状态的头痛,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 包,你就可以终身受益 :)

【讨论】:

没有多少人意识到这个回复的重要性。感谢您的视频链接:)【参考方案9】:

首屏: //发送数据到第二个屏幕

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

【讨论】:

它产生一个错误 Unhandled Exception: NoSuchMethodError: The getter 'focusScopeNode' was called on null. 我在异步函数中【参考方案10】:

最简单的方法

FirstPage.dart

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

//usernameController是String值,如果要传多个值全部加

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


【讨论】:

widget.usernameController 为我工作,但不仅仅是 usernameController,正如@Suragch 所说,我不知道这是因为不同的方法还是更新的颤振版本 如果我在 _PasswordPageState 类中引用 usernameController,我会收到错误:usernameController not defined。如果我使用widget.usernameController,则效果很好。【参考方案11】:

这个答案将涵盖向前传递数据和向后传递数据。与 Android 活动和 iOS 视图控制器不同,Flutter 中的不同屏幕只是小部件。在它们之间导航涉及创建称为路由的东西并使用Navigator 将路由推入和弹出堆栈。

将数据转发到下一个屏幕

要将数据发送到下一个屏幕,请执行以下操作:

    使SecondScreen 构造函数接受您要发送给它的数据类型的参数。在此特定示例中,数据定义为 String 值,并在此处设置为 this.text

    class SecondScreen extends StatelessWidget 
      final String text;
      SecondScreen(Key key, @required this.text) : super(key: key);
    
      ...
    

    然后使用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),
        ),
      ),
    );
  

将数据传回前一屏幕

在传回数据时,您需要做以下事情:

    FirstScreen 中,使用Navigatorasync 方法中推送(启动)SecondScreen,并等待它完成后返回的结果。

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

【讨论】:

如果我的第二个页面是有状态的小部件,我该怎么做?我无法在构造函数中调用 setState... @MrLalatg 使用 "widget.$variable_name" 如果您使用有状态小部件来获取数据。 String tempText = widget.text 这样发送任意大量数据可以吗?我知道在 Android 中,应尽量减少通过“活动”传递数据。 @user3245268,我不知道对数据量有任何限制。不过,我会假设,如果数据量非常大,那么您只需从第二个屏幕查询数据库或其他内容,而不是从第一个屏幕传递它。 如果用户按下移动设备的后退按钮而不是按下“RaisedButton”按钮,那么我们如何将 textFieldController.text 传递并发送回第一个屏幕?请提出建议。

以上是关于在 Flutter 中的屏幕之间传递数据的主要内容,如果未能解决你的问题,请参考以下文章

flutter:块间通信,不同块之间传递数据事件

Flutter:如何在两个小部件之间传递相同的数据?

如何在 FLutter 中使用 pushNamed 将数据从一个屏幕传递到另一个屏幕?

在 Flutter 中的小部件之间传递数据的最佳方式

在 Flutter 中将数据从一个屏幕传递到另一个屏幕是 null

将数据从另一个类传递到另一个类 onPressed Flutter