Flutter:动态表单(在不确定字段的情况下,生成动态表单)
关于数据模型:模型就是一种规范约束,便于维护管理,在不确定表单内会出现什么数据时,就没有模型一说。
这时就要用到动态表单(根据接口返回的字段,生成动态表单)
1、观察数据格式,定义模型
modelData:[
{
id:1,
name:'模型一',
ctaVal:{
"first_usdt": {"val": "10", "title": "首仓金额", "unit": "usdt", "read": "0" },
}
},
{
id:2,
name:'模型二',
ctaVal:{
"min_profit": {"val": "2", "title": "最小收益率", "unit": "%", "read": "1"}
}
}
]
上边自定义的数据格式,已知id和name为固定格式,
ctaVal内的数据不确定具体会返回什么信息,
接口从别的地方抓取的数据,不确定具体字段,
需要动态展示的数据定义:`final Map<String, dynamic> ctaVal;`
class DynamicFormModel {
int? id;
String? name;
final Map<String, dynamic> ctaVal;
DynamicFormModel ({
required this.id,
required this.name,
required this.ctaVal,
});
factory DynamicFormModel .fromJson(Map<String, dynamic> json) {
return StrategyListModel(
id: json['id'] as int?,
name: json['name'] as String?,
ctaVal: json['ctaVal'] ?? {},
);
}
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'ctaVal': ctaVal,
};
}
2、接口处理,请求数据,
// 模型列表
List<DynamicFormModel> modelList = [];
// 当前选中的模型id
int modelId = 0;
// 当前选中的模型
DynamicFormModel? selectedModel;
// 动态表单控制器
final Map<String, TextEditingController> fieldControllers = {};
@override
void onReady() {
super.onReady();
// 获取模型列表、默认模型为第一条数据
modelList = modelData.map((e) => DynamicFormModel .fromJson(e)).toList();
if (modelList.isNotEmpty) {
selectedModel = modelList.first;
modelController.text = selectedModel?.name ?? '';
modelId = selectedModel?.id ?? 0;
}
update(["strategy_add"]);
}
@override
void onClose() {
super.onClose();
// 释放所有控制器
fieldControllers.forEach((key, controller) {
controller.dispose();
});
}
void submit() async{
// 获取每个字段的值
for (var entry in fieldControllers.entries) {
if (entry.value.text.isEmpty) {
Loading.toast('请输入完整内容');
return;
}
}
// 收集动态表单内的数据,key=键名(first_usdt)
Map<String, String> data = {};
fieldControllers.forEach((key, controller) {
data[key] = controller.text;
print('$key,${controller.text}');
});
// 如果需要自定义字段
data['remark'] = remarkController.text;
// 转换为 JSON
String jsonData = jsonEncode(data);
print('jsonData = $jsonData');
}
3、动态渲染表单页面
// 表单
Widget _buildForm() {
return GetBuilder<StrategyAddController>(
id: "strategy_model",
builder: (_) {
return <Widget>[
controller.selectedModel == null
? const Center(child: Text('请选择一个模型'))
: <Widget>[
...controller.selectedModel!.ctaVal.entries.map((item) {
// 获取或创建控制器
final controllerKey = item.key;
if (!controller.fieldControllers.containsKey(controllerKey)) {
controller.fieldControllers[controllerKey] = TextEditingController(text: item.value['val']);
}
final fieldController = controller.fieldControllers[controllerKey];
return <Widget>[
InputWidget(
prefix: TextWidget.body(
item.value['title'],
),
placeholder: item.value['read'] == '1'
? ""
: '请输入${item.value['title']}',
controller:fieldController,
readOnly: item.value['read'] == '1' ? true : false,
).expanded(),
TextWidget.body(
item.value['unit'],
),
].toRow(crossAxisAlignment: CrossAxisAlignment.center).opacity(item.value['read'] == '1' ? 0.5 : 1);
}),
].toColumn(crossAxisAlignment: CrossAxisAlignment.start)
].toColumn();
},
);
}
...controller.selectedModel!.ctaVal.entries.map((item) => ...)
会遍历 ctaVal
中的每个条目,并将其映射为一个新的 Widget
列表。
每个 item
是一个 MapEntry
,可以通过 item.key
和 item.value
访问键和值。
这种方式非常适合动态生成 UI 组件,特别是在字段数量和内容不确定的情况下。