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

首次通过if语句添加TextField时FocusNode无法触发焦点问题

问题分析

核心问题在于你在GestureDetector的onTap回调中使用的isSelected是点击前的旧状态值:

  • 当点击未选中的项时,isSelected为false,因此不会执行焦点请求逻辑;
  • 点击后状态更新,UI重建并渲染TextField,但此时没有触发焦点请求;
  • 你之前添加的延迟和PostFrameCallback之所以无效,是因为逻辑本身基于旧状态,根本没进入焦点请求的代码分支。
解决方案
  • 修正焦点请求时机,基于新状态触发
    将焦点请求逻辑移到_selectedCategoryItem的UI重建流程中,当isSelected为true(即TextField已被渲染)时,自动请求焦点:

    Widget _selectedCategoryItem(SavingAllocationCategory category, int index, context) {
      final bool isSelected = index == _controller.selectedCategoryIndex;
    
      // 当当前项被选中时,在UI构建完成后请求焦点
      if (isSelected) {
        WidgetsBinding.instance.addPostFrameCallback((_) {
          // 确保焦点节点未被释放
          if (_controller.focusNode.mounted) {
            _controller.focusNode.requestFocus();
          }
        });
      }
    
      return GestureDetector(
        onTap: () {
          // 先取消原有焦点,避免键盘残留
          FocusScope.of(context).unfocus();
          // 修正拼写错误:onTapAllocationCateogry → onTapAllocationCategory
          _controller.onTapAllocationCategory(index);
        },
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 12),
          margin: const EdgeInsets.only(bottom: 16),
          height: 60,
          decoration: BoxDecoration(
              color: AppColors.boxColor, borderRadius: BorderRadius.circular(8)),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                category.name,
                style: const TextStyle(fontSize: 18),
              ),
              isSelected
                  ? Container(
                      width: 130,
                      decoration: BoxDecoration(
                          color: AppColors.inputColor,
                          borderRadius: BorderRadius.circular(8)),
                      child: TextField(
                        controller: _controller.savingTextController,
                        textDirection: TextDirection.rtl,
                        focusNode: _controller.focusNode,
                        keyboardType:
                            const TextInputType.numberWithOptions(decimal: true),
                        decoration: const InputDecoration(
                          contentPadding:
                              EdgeInsets.symmetric(horizontal: 8, vertical: 8),
                          hintTextDirection: TextDirection.rtl,
                          border: InputBorder.none,
                        ),
                      ),
                    )
                  : Container(
                      width: 130,
                      padding: const EdgeInsets.symmetric(horizontal: 5),
                      alignment: Alignment.centerRight,
                      decoration: BoxDecoration(
                          color: AppColors.inputColor,
                          borderRadius: BorderRadius.circular(8)),
                      child: Text(
                        formatCurrency(category.allocatedAmount),
                        style: const TextStyle(fontSize: 18),
                      ),
                    ),
            ],
          ),
        ),
      );
    }
    
  • 确保焦点节点正确管理
    在你的ViewModel中,确保focusNode被正确初始化和释放,避免内存泄漏:

    class YourController extends GetxController {
      final focusNode = FocusNode();
      final savingTextController = TextEditingController();
    
      @override
      void dispose() {
        focusNode.dispose();
        savingTextController.dispose();
        super.dispose();
      }
    
      // 你的其他方法...
      void onTapAllocationCategory(int index) {
        selectedCategoryIndex.value = index;
      }
    }
    
  • 可选:避免重复请求焦点(优化)
    如果担心每次重建都触发焦点请求,可以添加一个判断,仅当焦点未在当前节点时才请求:

    if (isSelected) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (_controller.focusNode.mounted && !_controller.focusNode.hasFocus) {
          _controller.focusNode.requestFocus();
        }
      });
    }
    
为什么这样有效
  • 当点击项更新selectedCategoryIndex后,Obx会触发UI重建,此时isSelected会基于新的状态值判断;
  • PostFrameCallback确保在当前帧的UI构建完成后再执行焦点请求,此时TextField已经被添加到Widget树中,焦点请求可以生效;
  • 修正拼写错误确保状态能正确更新,避免UI不重建的问题。

内容的提问来源于stack exchange,提问作者Don Huynh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 06:18:09