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

Flutter实现带复选框的动态TextFormField功能求解

Flutter 带复选框的动态输入框实现方案

你已经实现了动态添加TextFormField的基础,只需要在原有逻辑上扩展状态存储和UI层即可,具体步骤如下:


1. 定义条目数据模型

每个动态条目需要同时管理输入内容和选中状态,先创建一个模型类存储对应数据:

class InputItem {
  final TextEditingController controller;
  bool isSelected;

  InputItem({
    TextEditingController? controller,
    this.isSelected = false,
  }) : controller = controller ?? TextEditingController();

  // 销毁时释放控制器资源,避免内存泄漏
  void dispose() {
    controller.dispose();
  }
}

2. 改造页面状态逻辑

把原来存储TextEditingController的列表替换为存储InputItem的列表:

class DynamicInputPage extends StatefulWidget {
  const DynamicInputPage({super.key});

  @override
  State<DynamicInputPage> createState() => _DynamicInputPageState();
}

class _DynamicInputPageState extends State<DynamicInputPage> {
  // 初始化默认保留1个空输入框
  final List<InputItem> _inputItems = [InputItem()];

  // 添加新输入条目
  void _addNewItem() {
    setState(() {
      _inputItems.add(InputItem());
    });
  }

  // 删除指定索引的条目
  void _removeItem(int index) {
    setState(() {
      _inputItems[index].dispose();
      _inputItems.removeAt(index);
    });
  }

  // 工具方法:获取所有已选中、且内容非空的输入值
  List<String> getSelectedValues() {
    return _inputItems
        .where((item) => item.isSelected)
        .map((item) => item.controller.text.trim())
        .where((text) => text.isNotEmpty)
        .toList();
  }

  @override
  void dispose() {
    // 页面销毁时批量释放所有控制器资源
    for (var item in _inputItems) {
      item.dispose();
    }
    super.dispose();
  }
}

3. 构建列表项UI

每个条目用Row布局组合复选框、输入框和删除按钮,适配基础交互需求:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('动态带复选框输入框')),
    body: Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: _inputItems.length,
              itemBuilder: (context, index) {
                final item = _inputItems[index];
                return Padding(
                  padding: const EdgeInsets.symmetric(vertical: 8),
                  child: Row(
                    children: [
                      // 左侧复选框
                      Checkbox(
                        value: item.isSelected,
                        activeColor: Colors.blue,
                        onChanged: (value) {
                          setState(() {
                            item.isSelected = value ?? false;
                            // 可选:勾选后自动聚焦到对应输入框
                            if (item.isSelected) {
                              item.controller.selection = TextSelection.fromPosition(
                                TextPosition(offset: item.controller.text.length),
                              );
                              FocusScope.of(context).requestFocus(item.controller.focusNode);
                            }
                          });
                        },
                      ),
                      // 中间输入框
                      Expanded(
                        child: TextFormField(
                          controller: item.controller,
                          decoration: InputDecoration(
                            hintText: '请输入内容',
                            border: const OutlineInputBorder(),
                            // 选中时高亮输入框边框
                            enabledBorder: item.isSelected
                                ? OutlineInputBorder(
                                    borderSide: const BorderSide(color: Colors.blue, width: 2),
                                  )
                                : null,
                          ),
                        ),
                      ),
                      const SizedBox(width: 8),
                      // 右侧删除按钮,至少保留1个条目时隐藏删除按钮
                      if (_inputItems.length > 1)
                        IconButton(
                          icon: const Icon(Icons.delete, color: Colors.red),
                          onPressed: () => _removeItem(index),
                        ),
                    ],
                  ),
                );
              },
            ),
          ),
          const SizedBox(height: 16),
          // 底部操作区
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              ElevatedButton(
                onPressed: _addNewItem,
                child: const Text('添加输入框'),
              ),
              ElevatedButton(
                onPressed: () {
                  final selectedContent = getSelectedValues();
                  // 此处可自定义选中内容的处理逻辑,比如提交到后端
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('选中的内容:${selectedContent.join('、')}')),
                  );
                },
                child: const Text('获取选中内容'),
              ),
            ],
          )
        ],
      ),
    ),
  );
}

可选动效优化

如果要实现更流畅的勾选联动动效,可以用AnimatedContainer给边框加过渡动画:

// 把上面的TextFormField外层包裹AnimatedContainer即可
Expanded(
  child: AnimatedContainer(
    duration: const Duration(milliseconds: 200),
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(4),
      border: Border.all(
        color: item.isSelected ? Colors.blue : Colors.grey.shade400,
        width: item.isSelected ? 2 : 1,
      ),
    ),
    child: TextFormField(
      controller: item.controller,
      decoration: const InputDecoration(
        hintText: '请输入内容',
        border: InputBorder.none,
        contentPadding: EdgeInsets.all(12),
      ),
    ),
  ),
)

内容的提问来源于stack exchange,提问作者Alexander Santiago

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 12:18:03