如何构建返回Map列表的自定义FormBuilderField?
实现带自定义FormBuilderField的采购订单数量提交列表组件
需求:从后端获取数据列表,让用户为每条数据提交对应交付数量(类似采购订单报价后的数量选择场景)。现有FormListCard组件无法集成自定义FormBuilderField,以下是解决思路及重构方案:
原组件问题分析
原FormListCard硬编码使用KTextField作为输入组件,无法灵活替换为FormBuilder生态下的表单字段(如FormBuilderTextField、FormBuilderDropdown等),导致无法纳入表单统一管理。
重构思路与实现
1. 重构组件参数,支持自定义表单字段
将固定的KTextField参数替换为可定制的表单字段构建函数,同时增加表单字段名参数,确保每个列表项的字段能被FormBuilder识别。
2. 修改组件内部逻辑,适配FormBuilder规范
在组件内部优先使用外部传入的自定义表单字段,若未传入则默认创建数字输入类型的FormBuilderTextField,满足数量输入的基础需求。
重构后的代码示例
class FormListCard extends StatelessWidget { final String title; final String subTitle; final String fieldName; // 唯一表单字段名,用于FormBuilder关联数据 final Widget Function(String fieldName)? customField; // 自定义FormBuilderField构建函数 FormListCard({ required this.title, required this.subTitle, required this.fieldName, this.customField, }); @override Widget build(BuildContext context) { bool isLandScape = MediaQuery.of(context).orientation == Orientation.landscape; return Column( mainAxisSize: MainAxisSize.min, children: [ const Divider(), Row( children: [ Text( title, style: TextStyle( fontSize: isLandScape ? 18.sp : 32.sp, fontWeight: FontWeight.w600, ), ), const Spacer(), textInput(isLandScape), SizedBox(width: 20.w), Text( subTitle, style: TextStyle( fontSize: isLandScape ? 18.sp : 32.sp, fontWeight: FontWeight.w600, ), ) ], ) ], ); } Widget textInput(bool isLandScape) { // 优先使用自定义字段,否则默认创建数字输入框 final Widget field = customField?.call(fieldName) ?? FormBuilderTextField( name: fieldName, keyboardType: TextInputType.number, decoration: const InputDecoration(hintText: "输入数量"), validator: (value) { if (value == null || value.isEmpty) return "请输入数量"; if (int.tryParse(value) == null) return "请输入有效数字"; return null; }, ); return SizedBox( width: isLandScape ? 150.w : 300.w, height: isLandScape ? 150.h : 80.h, child: field, ); } }
使用示例
将组件包裹在FormBuilder内,为每条后端数据生成唯一的字段名,同时可自定义表单字段类型:
// 假设后端返回的订单数据结构 class OrderItem { final String id; final String name; final String unit; final int defaultQuantity; OrderItem({required this.id, required this.name, required this.unit, required this.defaultQuantity}); } // 表单使用示例 final _formKey = GlobalKey<FormBuilderState>(); List<OrderItem> orderItems = []; // 从后端获取的订单列表 @override Widget build(BuildContext context) { return FormBuilder( key: _formKey, child: Column( children: orderItems.map((item) { return FormListCard( title: item.name, subTitle: item.unit, fieldName: "quantity_${item.id}", // 唯一字段名,关联订单ID // 自定义FormBuilderTextField,可根据需求替换为其他组件 customField: (fieldName) => FormBuilderTextField( name: fieldName, keyboardType: TextInputType.number, initialValue: item.defaultQuantity.toString(), decoration: const InputDecoration( labelText: "交付数量", border: OutlineInputBorder(), ), ), ); }).toList(), ), ); } // 提交表单时获取所有数量数据 void submitForm() { if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); final formData = _formKey.currentState!.value; // 解析formData,根据fieldName匹配对应订单的数量 for (var item in orderItems) { final quantity = int.parse(formData["quantity_${item.id}"] ?? "0"); // 处理每条订单的数量数据 } } }
关键注意事项
- 所有
FormListCard必须包裹在FormBuilder组件内,才能实现表单状态统一管理。 - 每个列表项的
fieldName必须唯一,建议结合后端数据的唯一标识(如ID)生成,避免提交时数据混淆。 - 自定义字段时,只需传入符合
FormBuilderField规范的组件(需设置正确的name属性),即可无缝集成。
内容的提问来源于stack exchange,提问作者Youssef Idraiss
相关产品推荐
相关产品推荐

