TextField 和 BLoC - 值未更新或键盘已关闭

TextField and BLoC - value not updated or keyboard dismissed

我想在 Flutter 中结合使用 TextFieldBLoC package。目的是使 TextField 的内容与 BLoC 的相应 属性 保持同步。

因为 BLoC 基于 BlocBuilder 小部件,它基本上订阅 Stream 并在 Stream 发出新内容时更新和重新呈现所有受影响的子小部件,我想以下方法就足够了:

BlocBuilder<MyCubit, MyState>(
    builder: (BuildContext context, MyState state) {
        TextFormField(
          initialValue: state.model.title,
          onChanged: (String value) => myCubit.setTitle(value),
        ),
    }
);

实际上,它正在运行。当我希望能够覆盖来自 BLoC 的值时,问题就出现了。在我的例子中,我有一个 TextField 和一个 select 字段 (DropdownButton)。当用户 select 使用 DropdownButton 的值时,TextField 的内容应该被覆盖。 使用上述方法,当我在 DropdownButtononChange 中调用 setTitle(value) 时, Cubit 中的值被覆盖,但 TextField 保持不变.

然后我想到使用 TextEditingController 来处理 TextField 的文本(就像 Felix Angelov 建议的 here),更新 Cubit on change 并监听 Cubit:

中相应值的外部变化
BlocConsumer<MyCubit, MyState>(
    listener: (BuildContext context, MyState state) {
        if (state is InitialState) {
            _controller.addListener(() {
              myCubit.setTitle(_controller.text);
            });
            return;
        }

        _controller.text = state.title;
    },
    builder: (BuildContext context, MyState state) {
        TextFormField(
            controller: _controller,
        ), 
    }
);

如果我这样做,那么初始值由 Cubit 正确设置,当我使用 DropdownButton 更改它时,该值在 TextField 中更新并且它已更新在 TextField 中的文本发生变化时。但是,有一个问题:每次我在 TextField 中输入一个字符时,它就会失去焦点。 autofocus: true 救援?不幸的是没有。这样可以防止失去焦点,但是每当我输入内容时它都会跳转到输入文本的第一个字符。另外,我不希望 TextField 最初具有焦点。设置 FocusNode 也无济于事。 即使像建议的那样设置 UniqueKey 也不会改变什么。 关于FocusNodeonEditingComplete的想法也没有解决我的问题。

有没有人有一个 TextField 结合 BLoC 的工作解决方案,可以满足以下要求:

尽管研究了一段时间,但我仍然找不到解决方案。

我认为 TextField 的以下代码是正确的,只是您可以使用 TextEditingController 来设置其初始值。可以在initState里面设置controller的初始值,所以需要使用stateful widget

@override
  void initState() {
    super.initState();
    final state = BlocProvider.of<MyCubit>(context, listen: false);
    _controller = TextEditingController(text: state.model.title);
  }
BlocBuilder<MyCubit, MyState>(
    builder: (BuildContext context, MyState state) {
        TextFormField(
          controller: _controller,
          onChanged: (String value) => myCubit.setTitle(value),
        ),
    }
);

DropdownButtononChanged 中,除了在肘上调用 setTitle(value) 外,还使用其 controller 更改 TextField 的值.

(value){
    myCubit.setTitle(value);
    _controller.text = value;
}

唯一可能仍未解决的问题是调用 _controller.text = value; 时光标移动到开头 -- 在第一个字符之前。

在您稍后尝试使用 TextEditingController 更改 TextField 文本时,为什么您的 TextField 被包裹在 BlocBuilder 中?我相信这可以正确地从你的 BLoC:

更新 TextField 的文本
BlocListener<MyCubit, MyState>(
  listener: (BuildContext context, MyState state) {
    if(state.title != _controller.text) {
      _controller.text = state.title;
      _controller.selection = TextSelection.collapsed(offset: state.title.length);
    }
  },
  child: TextFormField(
    controller: _controller,
  ),
);

为了使用来自 TextField 的事件更新 BLoC 内部的值,请在 initState.

中注册 TextEditingController 的侦听器
@override
void initState() {
  super.initState();
  _controller.addListener(_changed);
}

@override
void dispose() {
  _controller.removeListener(_changed);
  super.dispose();
}

_changed() {
  myCubit.setTitle(_controller.text);
}