最近有人问我 flutter 前端如何处理动态表单。 这种是企业开发中的常见问题,特别是问卷和工作流审核表单。 今天我们就来实现下这个功能,主要是处理这个业务功能的思路。

flutter 中实现动态表单 form generator

视频

https://youtu.be/V-eEX_qmG-M

前言

最近有人问我 flutter 前端如何处理动态表单。

这种是企业开发中的常见问题,特别是问卷和工作流审核表单。

今天我们就来实现下这个功能,主要是处理这个业务功能的思路。

原文 https://ducafecat.com/blog/flutter-form-generate-dymic-data

参考

http://www.form-create.com/designer/

https://github.com/GavinZhuLei/vue-form-making

https://github.com/JakHuang/form-generator

https://github.com/xaboy/form-create

动态表单

动态表单是一种可以根据配置数据动态生成的表单,它的作用是允许用户在运行时根据不同的需求动态地创建和修改表单内容。动态表单可以在许多业务场景中使用,以下是一些示例:

  1. 数据录入和管理:在许多应用程序中,用户需要输入和管理各种数据。动态表单可以为应用程序提供灵活的数据输入和管理方案,允许用户根据需要动态添加、编辑和删除表单字段。
  2. 问卷和调查:动态表单是创建问卷和调查的理想方式。它可以允许用户根据需要动态添加和编辑问题、选项和其他信息,从而创建一个灵活的问卷和调查表单。
  3. 订单和支付:在电子商务应用程序中,用户通常需要填写订单和支付信息。动态表单可以为这些应用程序提供灵活的订单和支付表单,允许用户根据需要动态添加、编辑和删除字段。
  4. 审批流程:在许多企业中,审批流程是非常重要的。动态表单可以为这些企业提供灵活的审批表单,允许用户根据需要动态添加和编辑审批字段、审批流程和其他信息。

表单数据

[
  {
    "type": "input",
    "field": "Ff1s5zfzgmk0x",
    "title": "输入框",
    "info": "",
    "_fc_drag_tag": "input",
    "hidden": false,
    "display": true
  },
  {
    "type": "input",
    "field": "Fxf25zfzgn8kc",
    "title": "输入框",
    "info": "",
    "_fc_drag_tag": "input",
    "hidden": false,
    "display": true
  },
  {
    "type": "checkbox",
    "field": "Fn5j5zfzgnxt1",
    "title": "多选框",
    "info": "",
    "effect": {
      "fetch": ""
    },
    "options": [
      {
        "value": "1",
        "label": "选项1"
      },
      {
        "value": "2",
        "label": "选项2"
      }
    ],
    "_fc_drag_tag": "checkbox",
    "hidden": false,
    "display": true
  },
  {
    "type": "FcRow",
    "children": [
      {
        "type": "col",
        "props": {
          "span": 24
        },
        "children": [
          {
            "type": "input",
            "field": "Fih81nuhorr201",
            "title": "输入框",
            "info": "",
            "_fc_drag_tag": "input",
            "hidden": false,
            "display": true
          },
          {
            "type": "input",
            "field": "Fnac1nuhos3090",
            "title": "输入框",
            "info": "",
            "_fc_drag_tag": "input",
            "hidden": false,
            "display": true
          }
        ],
        "_fc_drag_tag": "col",
        "hidden": false,
        "display": true
      }
    ],
    "_fc_drag_tag": "row",
    "hidden": false,
    "display": true
  },
  {
    "type": "el-button",
    "children": [
      "按钮"
    ],
    "_fc_drag_tag": "el-button",
    "hidden": false,
    "display": true
  }
]

步骤

第一步:动态表单组件 DynamicForm

lib/form.dart

准备参数

  /// 表单数据
  final List<dynamic> data;

  /// 通用 文字输入框回调
  final Function(Map item, String value) textFormFieldChanged;

data 是压入的数据

textFormFieldChanged 是通用的组件处理回调函数

构建表单递归函数

  /// 构建表单
  List<Widget> _buildForm(List<dynamic> data) {
    List<Widget> widgets = [];
    for (var item in data) {
      switch (item['type']) {
        case 'input':
          widgets.add(TextFormField(
            decoration: InputDecoration(
              labelText: item['title'],
            ),
            onChanged: (value) => textFormFieldChanged(item, value),
          ));
          break;
        case 'checkbox':
          List<Widget> options = [];
          for (var option in item['options']) {
            options.add(Row(
              children: [
                Checkbox(
                  value: false,
                  onChanged: (value) {},
                ),
                Text(option['label']),
              ],
            ));
          }
          widgets.add(Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(item['title']),
              Column(children: options),
            ],
          ));
          break;
        case 'FcRow':
          List<Widget> children = _buildForm(item['children']);
          widgets.add(Row(children: children));
          break;
        case 'col':
          int span = item['props']['span'];
          List<Widget> colChildren = _buildForm(item['children']);
          if (span > 0 && span <= 24) {
            widgets.add(Expanded(
              flex: span,
              child: Column(children: colChildren),
            ));
          } else {
            widgets.add(Column(children: colChildren));
          }
          break;
        case 'el-button':
          widgets.add(ElevatedButton(
            onPressed: () {},
            child: Text(item['children'][0]),
          ));
          break;
        default:
          break;
      }
    }
    return widgets;
  }

children 节点表示子节点,我们用 _buildForm 函数嵌套方式执行

build 函数

  @override
  Widget build(BuildContext context) {
    return Column(
      children: _buildForm(data),
    );
  }

这是使用 Column ,因为一般表单数据还包含其它配置,如页头页尾,需要你去适配。

第二步:业务页面使用 FormPage

lib/page.dart

准备数据

  List<dynamic> jsonData = [
    {
      "type": "input",
      "field": "Ff1s5zfzgmk0x",
      "title": "输入框",
      "info": "",
      "_fc_drag_tag": "input",
      "hidden": false,
      "display": true
    },
    {
      "type": "input",
      "field": "Fxf25zfzgn8kc",
      "title": "输入框",
      "info": "",
      "_fc_drag_tag": "input",
      "hidden": false,
      "display": true
    },
    {
      "type": "checkbox",
      "field": "Fn5j5zfzgnxt1",
      "title": "多选框",
      "info": "",
      "effect": {"fetch": ""},
      "options": [
        {"value": "1", "label": "选项1"},
        {"value": "2", "label": "选项2"}
      ],
      "_fc_drag_tag": "checkbox",
      "hidden": false,
      "display": true
    },
    {
      "type": "FcRow",
      "children": [
        {
          "type": "col",
          "props": {"span": 24},
          "children": [
            {
              "type": "input",
              "field": "Fih81nuhorr201",
              "title": "输入框",
              "info": "",
              "_fc_drag_tag": "input",
              "hidden": false,
              "display": true
            },
            {
              "type": "input",
              "field": "Fnac1nuhos3090",
              "title": "输入框",
              "info": "",
              "_fc_drag_tag": "input",
              "hidden": false,
              "display": true
            }
          ],
          "_fc_drag_tag": "col",
          "hidden": false,
          "display": true
        }
      ],
      "_fc_drag_tag": "row",
      "hidden": false,
      "display": true
    },
    {
      "type": "el-button",
      "children": ["按钮"],
      "_fc_drag_tag": "el-button",
      "hidden": false,
      "display": true
    }
  ];

更新数据

  void setNewValue(String field, String value, List<dynamic> data) {
    for (var item in data) {
      if (item['field'] == field) {
        item['new_value'] = value;
        return;
      } else if (item.containsKey('children')) {
        setNewValue(field, value, item['children']);
      }
    }
  }

field 字段是每个组件的唯一标识

直接在后端给的数据结构中加入 new_value 属性,最后我们会一起传回服务器

通用 输入框回调函数

  void textFormFieldChanged(Map item, String value) {
    setNewValue(item["field"], value, jsonData);
  }

组件抛出业务层式,把整个 item 一起跑出

因为每个组件还配置了一定的业务配置

同时带上你的新数据

build函数

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Form Page'),
      ),
      // 动态表单
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: DynamicForm(
          data: jsonData,
          textFormFieldChanged: textFormFieldChanged,
        ),
      ),
    );
  }

最后:提交到服务器端

lib/page.dart

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Form Page'),

        // 保存提交到服务器
        actions: [
          IconButton(
            icon: const Icon(Icons.save),
            onPressed: () {
              if (kDebugMode) {
                print(jsonData);
              }
            },
          ),
        ],
      ),

      // 动态表单
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: DynamicForm(
          data: jsonData,
          textFormFieldChanged: textFormFieldChanged,
        ),
      ),
    );
  }

我们通过日志查看提交的数据

代码

https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_form_generater

小结

总之,动态表单可以在许多业务场景中使用,它可以为应用程序提供灵活的数据输入和管理方案,从而满足用户不同的需求。

在实际业务中客户端需要处理大量的动态业务,比如数据联动、卡片嵌套、内嵌导航,祝大家好运。

感谢阅读本文

如果我有什么错?请在评论中让我知道。我很乐意改进。


© 猫哥 ducafecat.com

end