flutter 中实现动态表单 form generator

news2024/12/28 3:48:59

flutter 中实现动态表单 form generator

前言

最近有人问我 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

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/714576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通用分页(下)

一.理解分页思想 1.分页的实质 1.1&#xff08;自我概述&#xff09; 用最简单的话来说&#xff0c;就是在一个界面展示不了那么多数据时&#xff0c;通过一点手段将数据一部分一部分的加载出来&#xff0c;从而减少服务器的压力。并且使能够让服务器利用率提高&#xff01; …

把yum源配置为ftp服务

配置yum源为ftp服务 一、在node1部署ftp服务器1.1 挂载镜像1.2 配置ftp的根目录 二、配置node2的yum源为node1的ftp服务器2.1 移除node2中默认的yum源文件2.2 创建新的yum源文件2.3 更新yum软件列表 环境介绍&#xff1a;有node1和node2两台Centos7虚拟机 环境准备&#xff1a;…

C++ 图的遍历

1. 图的遍历 给定一个图 G 和其中任意一个顶点 v0 &#xff0c;从 v0 出发&#xff0c;沿着图中各边访问图中的所有顶点&#xff0c;且每个顶 点仅被遍历一次 。 " 遍历 " 即对结点进行某种操作的意思 。 请思考树以前是怎么遍历的&#xff0c;此处可以直接用来遍…

手搓GPT系列之 - 通过理解LSTM的反向传播过程,理解LSTM解决梯度消失的原理 - 逐条解释LSTM创始论文全部推导公式,配超多图帮助理解(下篇)

本文承接上篇上篇在此和中篇中篇在此&#xff0c;继续就Sepp Hochreiter 1997年的开山大作 Long Short-term Memory 中APPENDIX A.1和A.2所载的数学推导过程进行详细解读。希望可以帮助大家理解了这个推导过程&#xff0c;进而能顺利理解为什么那几个门的设置可以解决RNN里的梯…

Spring中@NotEmpty、@NotBlank、@NotNull 的区别和使用

1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.0.5.RELEASE</version> </dependency>NotEmpty、NotBlank、NotNull 包的位置&#xff1…

i春秋,春秋云镜系列

目录 先提神CVE-2022-32991靶标介绍&#xff1a;复现&#xff1a; CVE-2022-30887靶标介绍&#xff1a;复现 先提神 CVE-2022-32991 靶标介绍&#xff1a; 该CMS的welcome.php中存在SQL注入攻击。 复现&#xff1a; 打开靶场地址&#xff0c;三个随便选一个进去&#xff0c…

Gof23设计模式之适配器模式

1.定义 将一个类的接口转换成客户希望的另外一个接口&#xff0c;使得原本由于接口不兼容而不能一起工作的那些类能一起工作。 适配器模式分为类适配器模式和对象适配器模式&#xff0c;前者类之间的耦合度比后者高&#xff0c;且要求程序员了解现有组件库中的相关组件的内部结…

国产划片机开创了半导体芯片切割的新工艺时代

国产划片机确实开创了半导体芯片切割的新工艺时代。划片机是一种用于切割和划分半导体芯片的设备&#xff0c;它是半导体制造过程中非常重要的一环。在过去&#xff0c;划片机技术一直被国外厂商所垄断&#xff0c;国内半导体制造企业不得不依赖进口设备。 然而&#xff0c;随着…

QT学习笔记6--信号之间的连接

连接 仍然使用connect函数&#xff0c;但是和函数重载类似&#xff0c;需要用到函数指针。如下所示 void (teacher:: *teachersignals)(void) &teacher::hungery;void (student:: *studentslots)(void) &student::treat;connect(zt,teachersignals,st,studentslots)…

水利三类人员专职安全生产管理人员c证安全生产法律法规考试题库

​本题库是根据最新考试大纲要求&#xff0c;结合近年来考试真题的重难点进行汇编整理组成的全真模拟试题&#xff0c;考生们可以进行专项训练&#xff0c;查漏补缺巩固知识点。本题库对热点考题和重难点题目都进行了仔细的整理和编辑&#xff0c;相信考生在经过了针对性的刷题…

浏览器使用Notification桌面通知消息推送

什么是 Notification&#xff1f; Notification 是浏览器最小化后在桌面显示消息的一种方法类似于 360 等流氓软件在桌面右下角的弹窗广告它与浏览器是脱离的&#xff0c;消息是置顶的 一、弹窗授权 授权当前页面允许通知可以通过检查只读属性 Notification.permission 的值来…

基于Springboot+Html的健身房管理系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着现代生活方式的改…

基于改进莱维飞行和混沌映射的金鹰优化算法(10种混沌映射随意切换),附matlab代码

“ 本篇文章对金鹰优化算法进行改进&#xff0c;首先通过引入混沌映射机制&#xff0c;对其群体进行初始化&#xff0c;增加金鹰个体的多样性&#xff1b;然后在金鹰个体的位置更新公式上引入改进的莱维飞行机制&#xff0c;提高搜索精度&#xff0c;帮助金鹰个体跳出局部最优。…

谈谈NLP中 大语言模型LLM的 思维链 Chain-of-Thought(CoT)

Chain-of-Thought(CoT) 1.介绍 在过去几年的探索中&#xff0c;业界发现了一个现象&#xff0c;在增大模型参数量和训练数据的同时&#xff0c;在多数任务上&#xff0c;模型的表现会越来越好。因而&#xff0c;现有的大模型LLM&#xff0c;最大参数量已经超过了千亿。 然而…

大数据Doris(五十五):BACKUP数据备份案例和注意事项

文章目录 BACKUP数据备份案例和注意事项 一、BACKUP数据备份案例 1、Doris中创建数据库&#xff0c;以及建表插入数据 2、创建远端仓库 3、全量备份指定 Doris 库下所有表所有分区数据 4、查看 backup 作业执行情况 5、查看远端仓库中已备份结果 二、注意事项 BACKUP数…

【花雕】青少年机器人技术等级考试理论综合试卷(一级)2021年9月

随着科技的不断进步&#xff0c;机器人技术已经成为了一个重要的领域。在这个领域中&#xff0c;机械结构是机器人设计中至关重要的一部分&#xff0c;它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说&#xff0c;了解机械结构的基础知识&#xff0c;掌…

Drools用户手册翻译——第二章 入门(下)测试和评估

因为篇幅原因&#xff0c;所以分为上下两个部分&#xff0c;主要就是通过一个交通违章项目的例子&#xff0c;带你先粗略感受一下决策模型的使用流程&#xff0c;总体来说有详细&#xff0c;也有没说清的地方&#xff0c;如果想要了解一下决策模型&#xff0c;可以进来了解一下…

[探地雷达]利用Faster RCNN对B-SCAN探地雷达数据进行目标检测

引用量较高的一篇会议论文。 由于真实雷达图像较少&#xff0c;作者采用了GPR工具箱&#xff0c;使用不同配置&#xff0c;合成了部分模拟雷达图。然后采用Cifar-10数据&#xff08;灰度图&#xff09;对Faster RCNN进行预训练&#xff0c;再采用真实和合成数据进行微调。 论…

(0021) H5-Vuejs配合 mint-ui 开发移动端web

mint-ui 初衷 element-ui主打pcweb&#xff0c;导致移动端上UI适配问题突出&#xff0c;趟了很多坑。这次更加理智些&#xff0c;选择了饿了么团队的主打移动端的mint-ui&#xff0c;目前来说体验很好。 认识Mint-ui 首先在手机上体验其demo&#xff0c;扫描链接&#xff1a;…

双层玻璃门碎了一面怎么更换

更换双层玻璃门的碎片需要按照以下步骤进行&#xff1a; 1. 备齐工具和材料&#xff1a;你需要准备以下工具和材料&#xff1a;安全手套、安全护目镜、扁头螺丝刀、绳子、玻璃胶和新的玻璃门。 2. 移除残存玻璃&#xff1a;首先&#xff0c;将门上的残留玻璃及其框架小心地取下…