繁体   English   中英

如何在 Flutter 中从其他 StatefulWidget 设置/更新 StatefulWidget 的状态?

[英]How to Set/Update State of StatefulWidget from other StatefulWidget in Flutter?

  1. 例如,在下面的代码中,加号按钮可以工作并且能够更新文本,但减号按钮不能。
  2. 但是如果我们按下 FloatingActionButton 然后状态被刷新。
  3. 减号按钮正在更改变量的值但不更新父小部件的状态。

在此处输入图像描述

这是代码.....

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

int number;

EdgeInsets globalMargin = const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0);
TextStyle textStyle = const TextStyle(
  fontSize: 100.0,
  color: Colors.black,
);

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    number = number ?? 0;
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Column(
        children: <Widget>[
          new Text(
            number.toString(),
            style: textStyle,
          ),
          new GridView.count(
            crossAxisCount: 2,
            shrinkWrap: true,
            scrollDirection: Axis.vertical,
            children: <Widget>[
              new InkResponse(
                child: new Container(
                    margin: globalMargin,
                    color: Colors.green,
                    child: new Center(
                      child: new Text(
                        "+",
                        style: textStyle,
                      ),
                    )),
                onTap: () {
                  setState(() {
                    number = number + 1;
                  });
                },
              ),
              new Sub(),
            ],
          ),
        ],
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: () {
          setState(() {});
        },
        child: new Icon(Icons.update),
      ),
    );
  }
}

class Sub extends StatefulWidget {
  @override
  _SubState createState() => new _SubState();
}

class _SubState extends State<Sub> {
  @override
  Widget build(BuildContext context) {
    return new InkResponse(
      child: new Container(
          margin: globalMargin,
          color: Colors.red,
          child: new Center(
            child: new Text(
              "-",
              style: textStyle,
            ),
          )),
      onTap: () {
        setState(() {
          number = number - 1;
        });
      },
    );
  }
}

1.On Child Widget : 添加参数 Function 参数

class ChildWidget extends StatefulWidget {
  final Function() notifyParent;
  ChildWidget({Key key, @required this.notifyParent}) : super(key: key);
}

2.在Parent Widget上:创建一个Function让孩子回调

refresh() {
  setState(() {});
}

3.On Parent Widget : 将 parentFunction 传递给 Child Widget

new ChildWidget( notifyParent: refresh );  

4.On Child Widget:调用父函数

  widget.notifyParent();

屏幕截图(父母对孩子,孩子对父母):

在此处输入图像描述

此示例显示调用方法

  1. 在父小部件的子小部件中定义。
  2. 在子小部件的父小部件中定义。

代码:

class ParentPage extends StatelessWidget {
  final GlobalKey<ChildPageState> _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Parent")),
      body: Center(
        child: Column(
          children: <Widget>[
            Expanded(
              child: Container(
                color: Colors.grey,
                width: double.infinity,
                alignment: Alignment.center,
                child: ElevatedButton(
                  child: Text("Call method in child"),
                  onPressed: () => _key.currentState!.methodInChild(), // calls method in child
                ),
              ),
            ),
            Text("Above = Parent\nBelow = Child"),
            Expanded(
              child: ChildPage(
                key: _key,
                function: methodInParent,
              ),
            ),
          ],
        ),
      ),
    );
  }

  methodInParent() => Fluttertoast.showToast(msg: "Method called in parent", gravity: ToastGravity.CENTER);
}

class ChildPage extends StatefulWidget {
  final VoidCallback function;

  ChildPage({Key? key, required this.function}) : super(key: key);

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

class ChildPageState extends State<ChildPage> {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.teal,
      width: double.infinity,
      alignment: Alignment.center,
      child: ElevatedButton(
        child: Text("Call method in parent"),
        onPressed: () => widget.function(), // calls method in parent
      ),
    );
  }

  methodInChild() => Fluttertoast.showToast(msg: "Method called in child");
}

旧解决方案

  1. 创建 _MyHomePageState 的全局实例。 在 _SubState 中将此实例用作 _myHomePageState.setState
  2. 无需创建全局实例。 相反,只需将父实例传递给子小部件

新解决方案:传递回调似乎比上述解决方案更好

根据颤振 2.0 更新的代码

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(),
    );
  }
}

EdgeInsets globalMargin =
    const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0);
TextStyle textStyle = const TextStyle(
  fontSize: 100.0,
  color: Colors.black,
);

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int number = 0;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('SO Help'),
      ),
      body: new Column(
        children: <Widget>[
          new Text(
            number.toString(),
            style: textStyle,
          ),
          new GridView.count(
            crossAxisCount: 2,
            shrinkWrap: true,
            scrollDirection: Axis.vertical,
            children: <Widget>[
              new InkResponse(
                child: new Container(
                    margin: globalMargin,
                    color: Colors.green,
                    child: new Center(
                      child: new Text(
                        "+",
                        style: textStyle,
                      ),
                    )),
                onTap: () {
                  setState(() {
                    this.number++;
                  });
                },
              ),
              new Sub(onTap: () {
                setState(() {
                  this.number--;
                });
              }),
            ],
          ),
        ],
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: () {
          setState(() {});
        },
        child: new Icon(Icons.update),
      ),
    );
  }
}

class Sub extends StatelessWidget {
  final Function onTap;
  Sub({this.onTap});

  @override
  Widget build(BuildContext context) {
    return new InkResponse(
      child: new Container(
        margin: globalMargin,
        color: Colors.red,
        child: new Center(
          child: new Text(
            "-",
            style: textStyle,
          ),
        ),
      ),
      onTap: this.onTap,
    );
  }
}

让我知道它是否有效。

我想扩展 Mohamed Elrashid 的答案,以防您需要将变量从子小部件传递给父小部件

在子小部件上:

class ChildWidget extends StatefulWidget {
  final Function() notifyParent;
  ChildWidget({Key key, @required this.notifyParent}) : super(key: key);
}

在父小部件上

void refresh(dynamic childValue) {
  setState(() {
    _parentVariable = childValue;
  });
}

在父窗口小部件上:将上面的函数传递给子窗口小部件

new ChildWidget( notifyParent: refresh ); 

在子小部件上:使用子小部件中的任何变量调用父函数

widget.notifyParent(childVariable);

旧的,但我会根据我的发现添加我的答案:

var ancestralState = context.findAncestorStateOfType<ParentState>();
      ancestralState.setState(() {
        // here you can access public vars and update state.
        ...
      });

这是对我有用的解决方案。

输出:购物车小部件的状态在添加项目时更新。

在此处输入图像描述

通过trigger from anywhere为要更新的小部件创建globalKey

final GlobalKey<CartWidgetState> cartKey = GlobalKey();

确保它保存在具有全局访问权限的文件中,以便可以从任何地方访问它。 我将它保存在 globalClass 中,其中通过应用程序的状态保存常用变量。

class CartWidget extends StatefulWidget {

  CartWidget({Key key}) : super(key: key);
  @override
  CartWidgetState createState() => CartWidgetState();
}

class CartWidgetState extends State<CartWidget> {
  @override
  Widget build(BuildContext context) {
    //return your widget
    return Container();
  }
}

从其他类调用您的小部件。

class HomeScreen extends StatefulWidget {

  HomeScreen ({Key key}) : super(key: key);
  @override
  HomeScreenState createState() => HomeScreen State();
}

class HomeScreen State extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return ListView(
              children:[
                 ChildScreen(), 
                 CartWidget(key:cartKey)
              ]
            );
  }
}



class ChildScreen extends StatefulWidget {

  ChildScreen ({Key key}) : super(key: key);
  @override
  ChildScreenState createState() => ChildScreen State();
}

class ChildScreen State extends State<ChildScreen> {
  @override
  Widget build(BuildContext context) {
    return InkWell(
              onTap: (){
                // This will update the state of your inherited widget/ class
                if (cartKey.currentState != null)
                    cartKey.currentState.setState(() {});
              },
              child: Text("Update The State of external Widget"),
           );
  }
}
class HomePage extends StatefulWidget {

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

class HomePageState extends State<HomePage> {

  int selectedIndex = 0;

   void setSelectedIndex(int index){
     setState(() {
      selectedIndex = index;
     });
  }
}

class TestPage extends StatefulWidget {

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

class TestPageState extends State<TestPage> {
  int selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
     return  GestureDetector(
                      onTap: (){

                        final HomePageState state = context.findAncestorStateOfType<HomePageState>();

                        state.setSelectedIndex(4);

                      },
                    child: Container(
                        width: 100,
                        height: 100,
                        color: Colors.green
              )
     );
  }

}

尽管这些先前的答案中的大多数都可以使用,但我建议您探索提供程序或 BloC 架构,这两种架构都已被 Google 推荐。

简而言之,后者将创建一个流,只要状态发生变化,就会向小部件树中的小部件报告,并且它会更新所有相关视图,而不管它是从哪里更新的。

这是一个很好的概述,您可以阅读以了解有关该主题的更多信息: https ://bloclibrary.dev/#/

对于那些想要在不使用第三方库的情况下在子级和父级之间来回传递变量的人来说,这是我用两个计数器按钮编写的示例。 当然,我不知道这是否是最佳实践,但我想提供我能创建的最简单的答案:

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: ParentWid(),
        ),
      ),
    );
  }
}

class ParentWid extends StatefulWidget {  
  @override
  State<ParentWid> createState() => _ParentWidState();
}

class _ParentWidState extends State<ParentWid> {
  int counter = 0;
  
  void refresh(int childValue) {
    setState(() {
      counter = childValue;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Column(children:[
       IconButton(
       onPressed: () {
         setState(() {
            counter++;
         });
       },
       icon: const Icon(Icons.add),
      ),
      ChildWid(counter, refresh)
    ]);
  }
}

class ChildWid extends StatefulWidget {
  final Function(int) notifyParent;
  final int? counter;
  const ChildWid(this.counter, this.notifyParent);

  @override
  State<ChildWid> createState() => _ChildWidState();
}

class _ChildWidState extends State<ChildWid> {
  @override
  Widget build(BuildContext context) {
    return Column(children:[
      Text(
        'Current Counter ${widget.counter ?? 0}',
        style: Theme.of(context).textTheme.headline4,
      ),
      IconButton(
       onPressed: () {
         int counterHolder = widget.counter ?? 0;
         counterHolder++;
         widget.notifyParent(counterHolder);
       },
       icon: const Icon(Icons.add),
      ),
    ]);
  }
}

这有点不正统,但它有效:您将不相关小部件的状态引用保存在一个公共对象中并相应地调用它们:

class Fuzz {
  State<A>? a;
  State<B>? b;
  int c = 0;
}



class A extends StatefulWidget {
  A(this.fuzz, {Key? key}) : super(key: key);
  Fuzz fuzz;
  @override
  State<A> createState() => _AState();
}

class _AState extends State<A> {
  @override
  void initState() {
    super.initState();
    widget.fuzz.a = this;
  }

  @override
  Widget build(BuildContext context) {
    return Center(
        child: TextButton(
      child: Text("More fuzz (${widget.fuzz.c})"),
      onPressed: () {
        widget.fuzz.b?.setState(() {
          widget.fuzz.c++;
        });
      },
    ));
  }
}

class B extends StatefulWidget {
  B(this.fuzz, {Key? key}) : super(key: key);
  Fuzz fuzz;
  @override
  State<B> createState() => _BState();
}

class _BState extends State<B> {
  @override
  void initState() {
    super.initState();
    widget.fuzz.b = this;
  }

  @override
  Widget build(BuildContext context) {
    return Center(
        child: TextButton(
      child: Text("Less fuzz (${widget.fuzz.c})"),
      onPressed: () {
        widget.fuzz.a?.setState(() {
          widget.fuzz.c--;
        });
      },
    ));
  }
}

 class TestView extends StatelessWidget {
  TestView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    var fuzz = Fuzz();
    return Scaffold(
        backgroundColor: Colors.black,
        body: Center(
            child: Row(
          children: [Expanded(child: A(fuzz)), Expanded(child: B(fuzz))],
        )));
  }
}

结果:

在此处输入图像描述

真正帮助我的是https://flutteragency.com/send-data-from-child-widget-to-its-parent/的文章,其中提到将数据从子小部件传递到父小部件,这在我的情况下也是需要的。

@Eli017 哇终于找到了一个可行且易于理解的解决方案......非常感谢......我一直在努力让我的下拉菜单从数据库中提供数据,并且只有一个下拉处理程序来传递一些 arguments进入....太棒了...我也将父母和孩子分成了两个文件,它马上就可以工作了....谢谢

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM