将数据传递到 Flutter 中的有状态小组件

问题描述 投票:0回答:9

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

我见过的两种款式是:

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
9个回答
526
投票

不要使用其构造函数将参数传递给

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

70
投票

最好的方法是不要使用 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)));

23
投票

另一个答案,基于@RémiRousselet的anwser和@user6638204的问题,如果你想传递初始值并且仍然能够稍后在状态中更新它们:

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

21
投票

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

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) - 它是
SatefullWidget
s 的更好、更干净的替代品:

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

3
投票

最佳实践是将有状态小部件类定义为immutable,这意味着将所有依赖项(到达参数)定义为最终参数。并在状态类中通过

widget.<fieldName>
访问它们。如果您想更改它们的值(例如重新分配),您应该在状态类中定义相同的类型属性,并在
initState
函数中重新分配它们。强烈建议不要在有状态小部件类中定义任何非最终属性,并将其设为mutable类。像这样的图案:

class SomePage extends StatefulWidget{
  final String? value;
  SomePage({this.value});

  @override
  State<SomePage> createState() => _SomePageState();
}

class _SomePageState extends State<SomePage> {
  String? _value;

  @override
  void initState(){
    super.initState();
    setState(() {
      _value = widget.value;
    });
  }

 @override
 Widget build(BuildContext context) {
    return Text(_value);
 }
}

1
投票

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

0
投票

如果需要直接向StatefulWidget传递参数,有两种方法可以实现,具体取决于你是否需要参数在State中是final的。

如果参数是final,我们可以直接在StatefulWidget中声明它,并使用“widget”前缀从State中调用它:

class ServerInfo extends StatefulWidget {
  final Server server;

  const ServerInfo({super.key, required this.server});

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

class _ServerInfoState extends State<ServerInfo> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(widget.server.name),
    );
  }
}

如果需要根据原始参数创建变量,则必须在使用“initState”从原始参数获取值的 State 中创建变量:

class ServerInfo extends StatefulWidget {
  final Server server;

  const ServerInfo({super.key, required this.server});

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

class _ServerInfoState extends State<ServerInfo> {
  late Server _server;

  @override
  void initState() {
    _server = widget.server;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(_server.name),
    );
  }
}

通过状态传递逻辑是不好的做法,这是这种做法的一个例子,我们应该避免它:

class ServerInfo extends StatefulWidget {
  final Server server;

  const ServerInfo({super.key, required this.server});

  @override
  State<ServerInfo> createState() => _ServerInfoState(server: server);
}

class _ServerInfoState extends State<ServerInfo> {
  late Server _server;

  _ServerInfoState({required Server server}) {
    _server = server;
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(_server.name),
    );
  }
}

按照前两个示例,我们可以通过小部件层次结构传递参数,但只能向下传递参数,如果我们需要向上传递参数,我们必须通过“Navigator.pop(context)”发送参数并捕获更高层的信息在层次结构中,但状态管理方法是更好的做法:

https://docs.flutter.dev/data-and-backend/state-mgmt/options


-1
投票

要将

data
传递给有状态小部件,首先,创建两个页面。现在从第一页打开第二页并传递数据。

class PageTwo extends StatefulWidget {
  final String title;
  final String name;

  PageTwo ({ this.title, this.name });

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

class PageTwoStateState extends State<PageTwo> {
  @override
  Widget build(BuildContext context) {
      return Text(
         widget.title,
         style: TextStyle(
               fontSize: 18, fontWeight: FontWeight.w700),
               ),
  }
}

class PageOne extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialButton(
          text: "Open PageTwo",
          onPressed: () {
                var destination = ServicePage(
                   title: '<Page Title>',
                   provider: '<Page Name>',
                );
                Navigator.push(context,
                    MaterialPageRoute(builder: (context) => destination));
                        },);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.