dart 如何从同级ChildWidgetB触发ChildWidgetA中的操作

fumotvh3  于 2023-03-27  发布在  其他
关注(0)|答案(1)|浏览(53)

情况:父小部件,有两个子部件。

如:

ParentWidget
 |-ChildWidgetA
 |-ChildWidgetB

ChildWidgetA包含一个数据输入表单,ChildWidgetB包含按钮(保存、取消、导出等)
ChildWidgetB中需要动作(点击按钮)触发ChildWidgetA中的响应(读取和保存文本数据)。
像这样更改父/子关系:

ParentWidget
 |-ChildWidgetA
    |-ChildWidgetB

...可以工作(所以子可以使用父提供的回调),但在我的实际情况中不可能,除非没有其他可能的解决方案。
我也宁愿将数据输入表单的知识拉到(不相关的)ParentWidget中,例如,在那里创建TextEditingController对象以帮助ControlsWidget读取文本值进行保存。

**问题:**如果我改变/重新排列代码,有什么可能性可以让它工作?(可能是流,事件侦听器,状态魔法...?)

下面是一些带注解的示例代码,演示了结构问题:

class ParentWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column( 
      children: [
        DataEntryFormWidget(),           // TextFields with values to read, save
        ControlsWidgetWithSaveButton(),  // Button I want to trigger the save
      ],
    );
  }
}

class DataEntryFormWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextFormField(), // assume I have a way to read values from these
        TextFormField(),
        // etc
      ]
    );
  }

  void doSave() {
    // read values from TextFormField()
    //    This is the thing I cannot do from ControlsWidgetWithSaveButton
    //    because that doesn't have access to TextFormField(s)... 
    //    So can't simply move this code/function to controls widget.
    // 
    // getDatabase.save(... the values ...)  
    // (all saved ok)
  }
}

class ControlsWidgetWithSaveButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        ElevatedButton(
          onPressed: () {
            // THE PROBLEM:
            // Here I want to invoke/trigger the doSave() function.
            // Could do the DB write here if I had the data... 
          },
          child: const Text('Save'),
        ),
        // ...  <assume some other buttons> 
      ]
    );
  }
}
kqqjbcuj

kqqjbcuj1#

一种方法是使用provider包。同样,你也可以使用其他状态管理包,但这是最简单的一个。
首先,我们需要创建一个ChangeNotifiersave方法将用于通知侦听器请求保存。

class FormController extends ChangeNotifier {
  void save() {
    notifyListeners();
  }
}

更改您的ParentWidget以注册提供程序

class ParentWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => FormController(),
      child: Column(
        children: [
          DataEntryFormWidget(), // TextFields with values to read, save
          ControlsWidgetWithSaveButton(), // Button I want to trigger the save
        ],
      ),
    );
  }
}

您需要将DataEntryFormWidget转换为StatefulWidget,因为我们需要处理initStatedispose。在initState中,我们将添加一个调用doSave方法的侦听器,在dispose中,我们将删除侦听器。

class DataEntryFormWidget extends StatefulWidget {
  @override
  State<DataEntryFormWidget> createState() => _DataEntryFormWidgetState();
}

class _DataEntryFormWidgetState extends State<DataEntryFormWidget> {
  @override
  void initState() {
    super.initState();
    Provider.of<FormController>(context, listen: false).addListener(doSave);
  }

  @override
  void dispose() {
    Provider.of<FormController>(context, listen: false).removeListener(doSave);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      TextFormField(),
      TextFormField(),
    ]);
  }

  void doSave() {
    // save
  }
}

最后,在ControlsWidgetWithSaveButton小部件的onPressed方法中,我们调用save来通知侦听器。

class ControlsWidgetWithSaveButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(children: [
      ElevatedButton(
        onPressed: () {
          Provider.of<FormController>(context, listen: false).save();
        },
        child: const Text('Save'),
      ),
    ]);
  }
}

相关问题