【问题标题】:Passing Data to a Stateful Widget in Flutter在 Flutter 中将数据传递给有状态的小部件
【发布时间】:2019-08-29 08:08:34
【问题描述】:

我想知道在创建状态小部件时将数据传递给有状态小部件的推荐方法是什么。

我见过的两种风格是:

class ServerInfo extends StatefulWidget {

  Server _server;

  ServerInfo(Server server) {
    this._server = server;
  }

  @override
    State<StatefulWidget> createState() => new _ServerInfoState(_server);
}

class _ServerInfoState extends State<ServerInfo> {
  Server _server;

  _ServerInfoState(Server server) {
    this._server = server;
  }
}

这种方法在ServerInfo_ServerInfoState中都保留了一个值,看起来有点浪费。

另一种方法是使用widget._server

class ServerInfo extends StatefulWidget {

  Server _server;

  ServerInfo(Server server) {
    this._server = server;
  }

  @override
    State<StatefulWidget> createState() => new _ServerInfoState();
}

class _ServerInfoState extends State<ServerInfo> {
  @override
    Widget build(BuildContext context) {
      widget._server = "10"; // Do something we the server value
      return null;
    }
}

这似乎有点倒退,因为状态不再存储在 _ServerInfoSate 中,而是存储在小部件中。

对此有最佳实践吗?

【问题讨论】:

标签: flutter dart statefulwidget pass-data


【解决方案1】:

不要使用 State 的构造函数将参数传递给它。 您只能使用this.widget.myField 访问参数。

不仅编辑构造函数需要大量的手工工作;它不会带来任何东西。没有理由复制Widget 的所有字段。

编辑:

这是一个例子:

class ServerIpText extends StatefulWidget {
  final String serverIP;

  const ServerIpText ({ Key? key, this.serverIP }): super(key: key);

  @override
  _ServerIpTextState createState() => _ServerIpTextState();
}

class _ServerIpTextState extends State<ServerIpText> {
  @override
  Widget build(BuildContext context) {
    return Text(widget.serverIP);
  }
}

class AnotherClass extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ServerIpText(serverIP: "127.0.0.1")
    );
  }
}

【讨论】:

  • 进一步说明,您通过构造函数传递给 State 对象的任何内容都不会得到更新!
  • 我在这里并且不明白评论。 “不要使用它的构造函数将参数传递给 State”。那么如何向 State 传递参数呢?
  • @Rexford State 已经使用widget 字段访问Stateful 的所有属性。
  • @RémiRousselet 如果我想使用 foo 预填充文本字段,并且仍然允许用户编辑它,该怎么办。我还应该在该州添加另一个 foo 属性吗?
  • 另一方面,将所有对象保留在小部件中而不是状态似乎很奇怪。
【解决方案2】:

最好的方法是不要使用它的构造函数将参数传递给 State 类。您可以使用 widget.myField 在 State 类中轻松访问。

举例

class UserData extends StatefulWidget {
  final String clientName;
  final int clientID;
  const UserData(this.clientName,this.clientID);

  @override
  UserDataState createState() => UserDataState();
}

class UserDataState extends State<UserData> {
  @override
  Widget build(BuildContext context) {
    // Here you direct access using widget
    return Text(widget.clientName); 
  }
}

导航屏幕时传递您的数据:

 Navigator.of(context).push(MaterialPageRoute(builder: (context) => UserData("WonderClientName",132)));

【讨论】:

    【解决方案3】:

    另一个答案,基于@RémiRousselet's anwser 和@user6638204's question,如果您想传递初始值并且以后仍然能够在状态下更新它们:

    class MyStateful extends StatefulWidget {
      final String foo;
    
      const MyStateful({Key key, this.foo}): super(key: key);
    
      @override
      _MyStatefulState createState() => _MyStatefulState(foo: this.foo);
    }
    
    class _MyStatefulState extends State<MyStateful> {
      String foo;
    
      _MyStatefulState({this.foo});
    
      @override
      Widget build(BuildContext context) {
        return Text(foo);
      }
    }
    

    【讨论】:

    • 我们可以直接使用initState来做类似foo = widget.foo的事情,不需要传递给构造函数
    • 如何将参数传递给这个?
    • @SteevJames 小部件MyStateful 有一个可选的命名参数(属性),您可以通过调用MyStateful(foo: "my string",) 创建此小部件
    • @Aqib initState 无法解决以下场景中的问题:例如,您使用空参数创建了 Statefull 小部件并且您正在等待数据加载。加载数据后,您希望使用新数据更新 Statefull 小部件,在这种情况下,当您调用 MyStatefull(newData) 时,不会调用 initState()!在这种情况下,didUpdateWidget(MyStatefull oldWidget) 将被调用,您需要将参数oldWidget.getData() 中的数据与widget.data 进行比较,如果不相同,请调用setState() 来重建小部件。
    • @kirill-karmazin 您能否详细说明无状态小部件解决方案?你会用什么代替?这是 Flutter 团队的最佳实践吗?谢谢
    【解决方案4】:

    用于传递初始值(不向构造函数传递任何内容)

    class MyStateful extends StatefulWidget {
      final String foo;
    
      const MyStateful({Key key, this.foo}): super(key: key);
    
      @override
      _MyStatefulState createState() => _MyStatefulState();
    }
    
    class _MyStatefulState extends State<MyStateful> {
      @override
      void initState(){
        super.initState();
        // you can use this.widget.foo here
      }
    
      @override
      Widget build(BuildContext context) {
        return Text(foo);
      }
    }
    

    【讨论】:

      【解决方案5】:

      Flutter 的有状态小部件 API 有点尴尬:将数据存储在 Widget 中以便在 build() 方法中访问它,该方法位于 State 对象中? 如果您不想使用一些更大的状态管理选项(Provider, BLoC),使用flutter_hooks(https://pub.dev/packages/flutter_hooks)——它是SatefullWidgets的更好更干净的替代品:

      class Counter extends HookWidget {
        final int _initialCount;
      
        Counter(this._initialCount = 0);
        
        @override
        Widget build(BuildContext context) {
          final counter = useState(_initialCount);
      
          return GestureDetector(
            // automatically triggers a rebuild of Counter widget
            onTap: () => counter.value++,
            child: Text(counter.value.toString()),
          );
        }
      }
      

      【讨论】:

        【解决方案6】:

        @Rémi Rousselet、@Sanjayrajsinh、@Daksh Shah 也更好。但我也定义了这是从起点。哪个参数是哪个值

           import 'package:flutter/material.dart';
            
            
            void main() => runApp(MyApp());
            
            class MyApp extends StatelessWidget {
              String name = "Flutter Demo";
              String description = "This is Demo Application";
            
              @override
              Widget build(BuildContext context) {
                return MaterialApp(
                  home: MainActivity(
                    appName: name,
                    appDescription: description,
                  ),
                );
              }
            }
            
            class MainActivity extends StatefulWidget {
              MainActivity({Key key, this.appName, this.appDescription}) : super(key: key);
              var appName;
              var appDescription;
            
              @override
              _MainActivityState createState() => _MainActivityState();
            }
            
            class _MainActivityState extends State<MainActivity> {
              @override
              Widget build(BuildContext context) {
                return Scaffold(
                  appBar: AppBar(
                    title: Text(widget.appName),
                  ),
                  body: Scaffold(
                    body: Center(
                      child: Text(widget.appDescription),
                    ),
                  ),
                );
              }
            }
        

        【讨论】:

          猜你喜欢
          • 2020-12-01
          • 2019-08-03
          • 2021-01-27
          • 1970-01-01
          • 1970-01-01
          • 2019-12-29
          • 2019-08-19
          • 2021-07-02
          相关资源
          最近更新 更多