NOTE: 相反,我最终重组了事物,消除了sibling 姐妹之间的沟通途径.

ParentWidget
 |-ChildWidgetA
   |-ChildWidgetB

因为我需要两个不同版本的ChildWidgetA(一个是静态的,一个带有编辑字段),并且因为我需要双向通信(sibling B向A发送命令,A将最终状态返回给B)……

一切都变得复杂起来.将"sibling " node 改为子 node ,从而使通信更容易.

教训:如果你需要sibling 姐妹之间的交流,那就努力想办法让他们成为父母/子元素.sibling 部件到sibling 部件的通信是可能的,但由于非常间接的代码流,使得代码很难跟踪.

--------- Original Post -----------

Situation:父部件,有两个子部件.

以下是:

ParentWidget
 |-ChildWidgetA
 |-ChildWidgetB

ChildWidgetA包含一个数据输入表单. ChildWidgetB包含按钮(保存、取消、导出等)

ChildWidgetB中的Need操作(按钮点击)以触发ChildWidgetA中的响应(文本数据的读取和保存).

按如下方式更改父/子关系:

ParentWidget
 |-ChildWidgetA
    |-ChildWidgetB

...可以工作(这样子元素可以使用父母提供的回调),但在我的实际情况下是不可能的,除非没有其他可能的解决方案.

我还宁愿not将数据输入表单的知识拉到(不相关的)ParentWidget中,例如,通过为那里创建TextEditingController个对象来帮助ControlsWidget读取文本值以进行保存.

Question:如果我更改/重新排列代码,有哪些可能性可以让它工作?(可能是流、事件侦听器、状态魔术…?)

以下是一些带注释的示例代码,演示了 struct 问题:

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

推荐答案

一种方法是使用provider套餐.同样,您也可以使用其他状态管理包完成此操作,但这是最简单的一个.

首先,我们需要创建一个ChangeNotifier,save方法将用于通知监听器已请求保存.

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

更改您的ParentWidget以注册Provider

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,因为我们需要处理initState dispose.在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'),
      ),
    ]);
  }
}

Flutter相关问答推荐

错误:找不到字体功能类型.Ffltter Google_Fonts包错误

在Flutter 中将参数从一个类传递到另一个类

带有可滚动页面(不包括分页区)的Flutter 页面视图

如何让两个展开的Widget根据其子窗口的屏幕大小

如何在flutter中更改showModalBottomSheet小部件中CheckboxListTile小部件的值?

无状态和有状态小部件,我可以同时使用吗?

升级到 Flutter 3.10 后,Flutter 键盘填充不起作用. Flutter 3.10 如何防止 BottomSheet 被键盘覆盖?

如何将此文本按钮剪辑在堆栈中的圆形头像上方?

Flutter - 如何将按钮对齐到行的右侧

如何为文本主题设置多种 colored颜色 ?

Flutter 构建方法使用的是旧版本的变量?

有没有办法为文本中的下划线创建自定义样式?

如何在 tabBar 中的选项卡之间制作垂直线?

如何在Flutter 中根据其父小部件高度设置图标按钮飞溅半径

NoSuchMethodError:方法 '[]' 在 null 上被调用.我的信息流中的错误

如何使用 Navigator 2.0 导航到任何页面?

在 Flutter 应用程序中全局使用 assets 文件夹

Flutter 评级栏包选中和未选中的容器

如何从 MemoryFileSystem 字节创建假 dart:io 文件?

将图像像素的灰度一维列表转换为灰度图像 Dart