Flutter动态添加/移除表单字段实现方案咨询
Flutter 实现动态表单字段(类似czMore功能)
不用找插件,自己实现其实很简单,核心就是用列表管理表单条目,配合setState或状态管理库刷新UI。下面是具体实现步骤和代码示例:
1. 定义表单数据模型
先把每个表单条目的数据封装成类,包含你需要的字段(设备类型、型号、解决方案),并设置默认值:
class DeviceSolution { String deviceType; String model; String solution; DeviceSolution({ this.deviceType = "默认设备类型", this.model = "默认型号", this.solution = "默认解决方案", }); }
2. 封装单个表单条目Widget
把一组字段封装成独立Widget,方便复用,同时提供回调更新数据:
class DeviceFormItem extends StatelessWidget { final DeviceSolution item; final Function(DeviceSolution) onUpdate; final Function()? onDelete; // 可选:添加删除功能 const DeviceFormItem({ super.key, required this.item, required this.onUpdate, this.onDelete, }); @override Widget build(BuildContext context) { final typeController = TextEditingController(text: item.deviceType); final modelController = TextEditingController(text: item.model); final solutionController = TextEditingController(text: item.solution); // 监听输入更新对应字段 typeController.addListener(() { onUpdate(DeviceSolution( deviceType: typeController.text, model: item.model, solution: item.solution, )); }); modelController.addListener(() { onUpdate(DeviceSolution( deviceType: item.deviceType, model: modelController.text, solution: item.solution, )); }); solutionController.addListener(() { onUpdate(DeviceSolution( deviceType: item.deviceType, model: item.model, solution: solutionController.text, )); }); return Card( margin: const EdgeInsets.symmetric(vertical: 8), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( decoration: const InputDecoration(labelText: "设备类型"), controller: typeController, ), TextField( decoration: const InputDecoration(labelText: "型号"), controller: modelController, ), TextField( decoration: const InputDecoration(labelText: "解决方案"), maxLines: 3, controller: solutionController, ), if (onDelete != null) TextButton.icon( onPressed: onDelete, icon: const Icon(Icons.delete), label: const Text("删除"), ), ], ), ), ); } }
3. 主页面管理表单列表
在主页面的State类里维护表单条目列表,实现「添加更多」按钮的逻辑:
class DynamicFormPage extends StatefulWidget { const DynamicFormPage({super.key}); @override State<DynamicFormPage> createState() => _DynamicFormPageState(); } class _DynamicFormPageState extends State<DynamicFormPage> { final List<DeviceSolution> _formItems = [DeviceSolution()]; // 初始一个条目 void _addNewItem() { setState(() { _formItems.add(DeviceSolution()); // 添加带默认值的新条目 }); } void _updateItem(int index, DeviceSolution newItem) { setState(() { _formItems[index] = newItem; }); } void _deleteItem(int index) { setState(() { _formItems.removeAt(index); // 至少保留一个条目 if (_formItems.isEmpty) _formItems.add(DeviceSolution()); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("动态设备表单")), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Expanded( child: ListView.builder( itemCount: _formItems.length, itemBuilder: (context, index) { return DeviceFormItem( item: _formItems[index], onUpdate: (newItem) => _updateItem(index, newItem), onDelete: _formItems.length > 1 ? () => _deleteItem(index) : null, ); }, ), ), const SizedBox(height: 16), ElevatedButton.icon( onPressed: _addNewItem, icon: const Icon(Icons.add), label: const Text("添加更多"), ), ], ), ), ); } }
4. 优化建议
- 表单验证:提交时遍历
_formItems,检查每个条目的字段是否符合要求,比如非空校验。 - 性能优化:如果条目数量多,
ListView.builder已经帮你做了懒加载,避免一次性渲染所有条目。 - 状态管理:如果表单逻辑复杂(比如和其他页面共享数据),可以用Provider、Riverpod替代
setState,让状态管理更清晰。 - 控制器优化:上面的示例每次重建
DeviceFormItem都会创建新的控制器,实际可以在Widget里缓存控制器,或者用ValueListenableBuilder减少不必要的重建。
内容的提问来源于stack exchange,提问作者Ernest Elikem
相关产品推荐
相关产品推荐

