You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Cubit中更新TextFormField异常:点击或API取数后光标跳转

问题描述

我通过Cubit实现表单验证,页面包含两个输入组件:可编辑的Address输入框,以及禁用状态的Remarks输入框。需求是点击Remarks输入框时,将Address输入框的值设置为"Hello"。

当给Address的TextField添加以下代码后,点击Remarks确实能成功赋值,但修改Address内容时出现异常:每次删除或输入字符,光标都会跳回输入框起始位置。

controller: TextEditingController(
            text: state.formFields['address']?.value,
          ),
问题原因

每次Cubit状态更新触发BlocBuilder重建时,都会创建一个新的TextEditingController实例。新的控制器会重置光标位置到文本开头,导致输入体验异常。

解决方案

将_AddressInput从无状态组件改为有状态组件,在组件内部持有唯一的TextEditingController实例,通过监听Cubit状态变化来更新控制器文本,而非每次重建都新建控制器。

修改后的_AddressInput代码

class _AddressInput extends StatefulWidget {
  @override
  State<_AddressInput> createState() => _AddressInputState();
}

class _AddressInputState extends State<_AddressInput> {
  late TextEditingController _controller;

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController();
    // 初始化获取Address的初始值
    final cubit = context.read<GenericFormFieldCubit>();
    final initialValue = (cubit.state.formFields['address'] as UserNameInputField).value;
    _controller.text = initialValue;
  }

  @override
  void dispose() {
    _controller.dispose(); // 销毁控制器避免内存泄漏
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<GenericFormFieldCubit, GenericFormFieldState>(
      listenWhen: (previous, current) =>
          previous.formFields['address'] != current.formFields['address'],
      listener: (context, state) {
        final latestValue = (state.formFields['address'] as UserNameInputField).value;
        // 仅当值不同时更新,避免不必要的光标跳动
        if (_controller.text != latestValue) {
          _controller.text = latestValue;
        }
      },
      child: TextField(
        controller: _controller,
        key: const Key('personalInfoForm_addressInput_textField'),
        onChanged: (value) {
          final name = UserNameInputField.custom(
            value,
            errorMessage: '请输入有效地址',
            requiredMessage: '此字段为必填项',
            minValue: '5',
            maxValue: '20',
          );
          context.read<GenericFormFieldCubit>().updateField('address', name);
        },
        decoration: InputDecoration(
          labelText: '地址',
          errorText: (context.read<GenericFormFieldCubit>().state.formFields['address'] as UserNameInputField).invalid
              ? (context.read<GenericFormFieldCubit>().state.formFields['address'] as UserNameInputField).getErrorMessage
              : null,
        ),
      ),
    );
  }
}

额外优化:简化RemarksInput代码

由于Remarks输入框是禁用且只读状态,无需监听自身状态变化,可以移除多余的BlocBuilder:

class _RemarksInput extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        context.read<GenericFormFieldCubit>().updateField('address', UserNameInputField.custom("Hello"));
      },
      child: TextField(
        enabled: false,
        readOnly: true,
        key: const Key("personalInfoForm_remarksInput_textField"),
        decoration: const InputDecoration(
          labelText: '备注',
        ),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Rabindra Acharya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 18:09:21