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

如何将自定义表单的JSON转换为Flutter Widgets并在APP中展示?

自定义JSON表单转Flutter Widget的实现方案

核心思路

既然现有Flutter包适配不了你的JSON结构,最靠谱的方式是自己写专属解析器,根据你的表单JSON字段直接映射对应的Flutter Widget。

具体步骤

1. 明确JSON结构细节

先把你的表单JSON拆解清楚,列出每个控件的通用字段,比如:

  • type:控件类型(text_input、checkbox、select等)
  • label:控件显示标签
  • name:后台绑定的字段名
  • required:是否为必填项
  • options:选择类控件的选项列表
  • 其他自定义属性(比如输入长度限制、默认值)

举个你的JSON示例(假设结构):

{
  "formId": "user_info_form",
  "fields": [
    {
      "type": "text_input",
      "label": "手机号",
      "name": "phone",
      "required": true,
      "maxLength": 11
    },
    {
      "type": "radio_group",
      "label": "性别",
      "name": "gender",
      "options": ["男", "女", "保密"]
    }
  ]
}

2. 编写Widget生成工具类

创建一个FormWidgetBuilder类,根据字段类型返回对应的Flutter Widget:

class FormWidgetBuilder {
  static Widget buildField(Map<String, dynamic> field) {
    final type = field['type'] as String;
    final label = field['label'] as String;
    final required = field['required'] as bool? ?? false;

    switch (type) {
      case 'text_input':
        return TextFormField(
          decoration: InputDecoration(
            labelText: required ? '$label*' : label,
            border: const OutlineInputBorder(),
          ),
          maxLength: field['maxLength'] as int?,
          validator: required ? (value) => value?.isEmpty ?? true ? '请输入$label' : null : null,
        );
      case 'radio_group':
        final options = (field['options'] as List).cast<String>();
        String? selectedValue;
        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(required ? '$label*' : label),
            ...options.map((opt) => RadioListTile(
                  title: Text(opt),
                  value: opt,
                  groupValue: selectedValue,
                  onChanged: (val) => selectedValue = val,
                )),
          ],
        );
      case 'select':
        final options = (field['options'] as List).cast<String>();
        String? selectedValue;
        return DropdownButtonFormField<String>(
          decoration: InputDecoration(
            labelText: required ? '$label*' : label,
            border: const OutlineInputBorder(),
          ),
          items: options.map((opt) => DropdownMenuItem(value: opt, child: Text(opt))).toList(),
          onChanged: (val) => selectedValue = val,
        );
      // 其他控件类型(日期选择、文件上传等)依次添加case
      default:
        return Text('不支持的控件类型:$type');
    }
  }
}

3. 加载JSON生成完整表单

在页面中加载JSON(从API或本地读取),遍历字段生成完整表单:

class CustomFormPage extends StatelessWidget {
  final Map<String, dynamic> formJson;

  const CustomFormPage({super.key, required this.formJson});

  @override
  Widget build(BuildContext context) {
    final fields = (formJson['fields'] as List).cast<Map<String, dynamic>>();
    return Scaffold(
      appBar: AppBar(title: Text(formJson['formId'] as String)),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: ListView(
          children: [
            ...fields.map(FormWidgetBuilder.buildField),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                // 处理表单提交逻辑,收集所有字段值
              },
              child: const Text('提交表单'),
            ),
          ],
        ),
      ),
    );
  }
}

4. 表单状态管理

如果需要保存用户输入,建议用Form组件+GlobalKey<FormState>,或者用Riverpod、Provider这类状态管理库统一管理字段值。比如给输入控件绑定控制器:

// 修改buildField方法,传入控制器
static Widget buildField(Map<String, dynamic> field, TextEditingController controller) {
  // ...
  case 'text_input':
    return TextFormField(
      controller: controller,
      // 其他属性保持不变
    );
}

注意事项

  • 先枚举所有可能的控件类型,避免遗漏场景;
  • 复杂控件(如日期选择、文件上传)单独处理交互逻辑;
  • JSON结构变动时,只需修改FormWidgetBuilder对应的case,无需改动页面代码;
  • 可把通用的样式(如输入框装饰器)抽成全局方法,减少重复代码。

内容的提问来源于stack exchange,提问作者ahmad hamoush

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 21:40:27