父小部件中的setState不会更新子项的值

时间:2019-04-10 11:42:46

标签: dart flutter

我有一个StatefulWidget,其中有一个ListView包含几个子代小部件。

其中一个孩子是GridView,其中包含一些物品。

我想要实现的是在从“父级”窗口小部件中按下按钮时重建这个GridView子级。该按钮位于“父”窗口小部件的bottomNavigationBar中。

但是,当我按下按钮时,它应该转到有效的_resetFilter()方法。但是setState()似乎没有更新Child小部件中的GridView build()方法。

class ParentState extends State<Parent> {

  // removed for brevity

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(...),
      bottomNavigationBar: BottomAppBar(
        child: new Row(
          children: <Widget>[
            Padding(
                padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 5.0),
                child: SizedBox(
                  onPressed: () {
                    _resetFilter();
                  },
                )
            ),
          ],
        ),
      ),
      body: Container(
        child: Form(
            key: _formKey,
            child: ListView(
              children: <Widget>[
                Column(
                  children: <Widget>[
                    Container(
                      child: Column(
                        children: <Widget>[
                          Container(...), // this works
                          Column(...),
                          Container(...), // this works
                          Container( 
                            child: GridView.count(
                              // ...
                              children:
                                  List.generate(oriSkills.length, (int i) {

                                bool isSkillExist = false;

                                if (_selectedSkills.contains(rc.titleCase)) {
                                  isSkillExist = true;
                                } else {
                                  isSkillExist = false;
                                }
                                return Child( // this doesn't work
                                  id: oriSkills[i]['id'],
                                  name: oriSkills[i]['description'],
                                  skillSelect: isSkillExist, // this boolean showed correct value from the above logic
                                  onChange: onSkillChange,
                                );
                              }),
                            ),
                          ),
                        ],
                      ),
                    )
                  ],
                )
              ],
            )),
      ),
    );
  }

  void _resetFilter() {
    setState(() {
      _theValue = 0.0;
      searchC.text = "";
      _selectedSkills = []; // this is the variable that I'd like the GridView to recreate from.
    });
  }
}

我试图在Child小部件中打印一个字段名,但是它总是显示旧值而不是新值。

即使按下按钮后,它也确实将正确的值传递给ChildState

class ChildState extends State<Child> {
  final String name;
  final MyCallbackFunction onChange;
  bool skillSelect;
  double size = 60.0;

  ChildState({this.name, this.skillSelect, this.onChange});

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  void setSkillLevel() {
    setState(() {
      if (skillSelect) {
        skillSelect = false;
        onChange(name, false);
      } else {
        skillSelect = true;
        onChange(name, true);
      }
    });
  }

  Color _jobSkillSelect(bool select) {
    print(select); // always print old state instead of new state
    return select ? Color(MyColor.skillLvlOne) : Color(MyColor.skillDefault);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        child: Column(children: <Widget>[
      InkResponse(
          onTap: setSkillLevel,
          child: Container(
            height: size,
            width: size,
            decoration: BoxDecoration(
              image: DecorationImage(
                colorFilter: ColorFilter.mode(_jobSkillSelect(skillSelect), BlendMode.color),
              ),
            ),
          )),
    ]));
  }
}

如何在按下重置按钮后更新子级小部件以使其具有父级小部件的更新值?

1 个答案:

答案 0 :(得分:1)

您可能希望将值传递给实际的Child类。 不是到其状态。
一旦您的父母重建,这个班就是重建。因此,新的价值将得到体现。

因此,您的Child实现应如下所示(不要忘记将onChange类型替换为自定义函数。

class Child extends StatefulWidget {
  final String name;
  final Function(void) onChange;
  final bool skillSelect;
  final double size;
  final Function(bool) onSkillLevelChanged;

  const Child({Key key, this.name, this.onChange, this.skillSelect, this.size, this.onSkillLevelChanged}) : super(key: key);

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

class _ChildState extends State<Child> {
  Color _jobSkillSelect(bool select) {
    print(select); // always print old state instead of new state
    return select ? Color(MyColor.skillLvlOne) : Color(MyColor.skillDefault);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          InkResponse(
              onTap: () {
                if (widget.onSkillLevelChanged != null) {
                  widget.onSkillLevelChanged(!widget.skillSelect);
                }
              },
              child: Container(
                height: widget.size,
                width: widget.size,
                decoration: BoxDecoration(
                  image: DecorationImage(
                    colorFilter: ColorFilter.mode(_jobSkillSelect(widget.skillSelect), BlendMode.color),
                  ),
                ),
              )),
        ],
      ),
    );
  }
}

在这种情况下,Child不再负责管理其skillSelect属性。它只是在其父节点上调用Function。然后,父级将使用新的skillSelect布尔值进行构建。

所以您可以这样使用这个孩子:

return Child( // this doesn't work
   id: oriSkills[i]['id'],
   name: oriSkills[i]['description'],
   skillSelect: oriSkills[i]['isSkillExist'], 
   onChange: onSkillChange,
   onSkillLevelChanged: (newSkillLevel) {
      setState(() {
         oriSkills[i]['isSkillExist'] = newSkillLevel;
      });
   },
);