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

Flutter如何基于GET API动态生成多类型输入表单并提交数据

Flutter 动态表单实现方案

实现逻辑完全基于Flutter原生组件,无需引入第三方依赖,后续扩展控件类型、校验规则都非常方便。

1. 定义数据实体类

先把接口返回的JSON结构转换为强类型Dart实体,避免后续取值出现类型错误。注意示例接口中控件类型的键名存在拼写错误(写为cotrollType),代码里做了双向兼容,后端修正键名后也能正常运行。

class QuestionItem {
  final String question; // 题目标题/输入字段提示
  final String controlType; // 控件类型
  final List<String> values; // 选项集合
  final List<String> validations; // 校验规则列表

  QuestionItem({
    required this.question,
    required this.controlType,
    required this.values,
    required this.validations,
  });

  factory QuestionItem.fromJson(Map<String, dynamic> json) {
    return QuestionItem(
      question: json['question'] as String,
      // 兼容接口拼写错误的键名
      controlType: (json['cotrollType'] ?? json['controlType']) as String,
      values: List<String>.from(json['values'] ?? []),
      validations: (json['validations'] as List? ?? [])
          .map((e) => e['validation'] as String)
          .toList(),
    );
  }
}

2. 核心逻辑说明

用Flutter自带的Form组件做全局表单状态管理,通过控件类型字段动态匹配要渲染的输入组件,同时把接口返回的校验规则统一接入表单校验流程:

  • 控件类型为textField时,渲染文本输入框
  • 控件类型为dropdown时,渲染下拉选择框
  • 后续需要支持checkbox、图片上传、文件上传等控件时,只需要新增类型判断分支即可
  • 校验逻辑统一收口,新增长度校验、格式校验等规则只需要加对应判断分支,不需要修改每个控件的单独代码

3. 完整页面实现代码

import 'package:flutter/material.dart';

class DynamicFormPage extends StatefulWidget {
  const DynamicFormPage({super.key, required this.questionList});
  // 接口拉取后转好的问题实体列表
  final List<QuestionItem> questionList;

  @override
  State<DynamicFormPage> createState() => _DynamicFormPageState();
}

class _DynamicFormPageState extends State<DynamicFormPage> {
  final _formKey = GlobalKey<FormState>();
  // 存储所有题目的用户答案,key为题目索引
  final Map<int, dynamic> _formValue = {};

  // 统一校验方法
  String? _validateInput(dynamic value, List<String> rules) {
    for (final rule in rules) {
      switch (rule) {
        case 'IsRequired':
          if (value == null || 
              (value is String && value.trim().isEmpty) || 
              (value is List && value.isEmpty)) {
            return '该字段为必填项';
          }
          break;
        // 新增校验规则直接在这里加case即可
      }
    }
    return null;
  }

  // 根据控件类型渲染对应输入组件
  Widget _buildFormControl(int index, QuestionItem item) {
    switch (item.controlType) {
      case 'textField':
        return TextFormField(
          decoration: InputDecoration(
            labelText: item.question,
            border: const OutlineInputBorder(),
          ),
          validator: (value) => _validateInput(value, item.validations),
          onSaved: (value) => _formValue[index] = value?.trim(),
        );

      case 'dropdown':
        return DropdownButtonFormField<String>(
          decoration: InputDecoration(
            labelText: item.question,
            border: const OutlineInputBorder(),
          ),
          items: item.values
              .map((option) => DropdownMenuItem(
                    value: option,
                    child: Text(option),
                  ))
              .toList(),
          validator: (value) => _validateInput(value, item.validations),
          onChanged: (value) => _formValue[index] = value,
          onSaved: (value) => _formValue[index] = value,
        );

      // 扩展其他控件直接在这里加case
      // case 'checkbox':
      // case 'image':
      // case 'file':
      default:
        return Text('暂不支持的控件类型: ${item.controlType}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动态测验')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: ListView.separated(
            itemCount: widget.questionList.length,
            separatorBuilder: (_, __) => const SizedBox(height: 20),
            itemBuilder: (context, index) {
              final question = widget.questionList[index];
              return _buildFormControl(index, question);
            },
          ),
        ),
      ),
      bottomNavigationBar: Padding(
        padding: const EdgeInsets.all(16.0),
        child: ElevatedButton(
          onPressed: () {
            // 提交时先触发全量校验
            if (_formKey.currentState?.validate() ?? false) {
              _formKey.currentState?.save();
              // _formValue就是所有题目的答案,直接传给提交接口即可
              debugPrint('表单提交数据: $_formValue');
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('提交成功')),
              );
            }
          },
          child: const Text('提交答案'),
        ),
      ),
    );
  }
}

4. 使用方式

接口请求拿到返回的JSON数组后,遍历数组通过QuestionItem.fromJson方法转成实体列表,传入DynamicFormPage即可自动渲染出对应表单。

内容的提问来源于stack exchange,提问作者Emmanuel Agbo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 17:57:33