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
相关产品推荐
相关产品推荐

