flutter 中 Form 和 FormField 实现表单验证

flutter 中 FormField 实现有哪些?

 在Flutter中,可以通过使用`FormField`类来实现表单字段的输入和验证。以下是一些常见方式可以使用`FormField`类来实现不同类型的表单字段:

TextFormField:

  • 使用TextFormField可以创建一个文本输入框,用于用户输入文本数据。
  • 示例代码:
FormField(
  builder: (FormFieldState<String> state) {
    return TextFormField(
      onChanged: (value) {
        state.didChange(value);
      },
      validator: (value) {
        if (value.isEmpty) {
          return '请输入文本';
        }
        return null;
      },
    );
  },
)

CheckboxFormField:

  • CheckboxFormField用于创建一个复选框,用户可以选择或取消选择该复选框。

   - 示例代码:

FormField(
  builder: (FormFieldState<bool> state) {
    return CheckboxListTile(
      title: Text('同意'),
      value: state.value ?? false,
      onChanged: (value) {
        state.didChange(value);
      },
    );
  },
)

DropdownButtonFormField:

  • DropdownButtonFormField允许用户从下拉菜单中选择一个选项。

示例代码:

FormField(
  builder: (FormFieldState<String> state) {
    return DropdownButtonFormField<String>(
      value: state.value,
      items: ['选项1', '选项2', '选项3'].map((String value) {
        return DropdownMenuItem<String>(
          value: value,
          child: Text(value),
        );
      }).toList(),
      onChanged: (value) {
        state.didChange(value);
      },
    );
  },
)

DateFieldFormField:

  • 可以使用第三方库如intl来创建具有日期选择功能的表单字段。

示例代码:

FormField(
  builder: (FormFieldState<DateTime> state) {
    return DateTimeField(
      format: DateFormat("yyyy-MM-dd"),
      onShowPicker: (context, currentValue) {
        return showDatePicker(
          context: context,
          initialDate: currentValue ?? DateTime.now(),
          firstDate: DateTime(2000),
          lastDate: DateTime(2100),
        );
      },
      onChanged: (value) {
        state.didChange(value);
      },
    );
  },
)

通过使用这些不同类型的FormField类,可以轻松实现各种表单字段的输入和验证功能,从而创建交互性强大且用户友好的表单。

实现radio

FormField(
  builder: (FormFieldState<String> state) {
    return Column(
      children: <Widget>[
        RadioListTile<String>(
          title: Text('选项1'),
          value: 'option1',
          groupValue: state.value,
          onChanged: (value) {
            state.didChange(value);
          },
        ),
        RadioListTile<String>(
          title: Text('选项2'),
          value: 'option2',
          groupValue: state.value,
          onChanged: (value) {
            state.didChange(value);
          },
        ),
        RadioListTile<String>(
          title: Text('选项3'),
          value: 'option3',
          groupValue: state.value,
          onChanged: (value) {
            state.didChange(value);
          },
        ),
        // 添加验证逻辑
        if (state.hasError)
          Text(
            state.errorText,
            style: TextStyle(color: Colors.red),
          ),
      ],
    );
  },
  validator: (value) {
    if (value == null) {
      return '请选择一个选项';
    }
    return null;
  },
)
DropdownButtonFormField、DateFieldFormField、CheckboxFormField、TextFormField

其实内部就是对FormField(builder: 方法 进行传递 只是一个简单封装

Form 如何通过key 进行验证 

final _formKey = GlobalKey<FormState>();


 bool isPass =(formKey.currentState as FormState).validate();

发布页面代码参考

publish_task_binding.dart

import 'package:get/get.dart';

import 'publish_task_logic.dart';

class PublishTaskBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut<PublishTaskLogic>(() => PublishTaskLogic());
  }
}

 publish_task_logic.dart

import 'package:LS/common/global.dart';
import 'package:LS/controller/self.dart';
import 'package:LS/utils/toast.dart';
import 'package:LS/widgets/flutter_pickers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_pickers/style/picker_style.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';

class PublishTaskLogic extends GetxController {
  int get endDaysLimit {
    return int.parse(SelfController.find.sysConfig[56] ?? '0');
  }

  // 是否是招标任务
  bool get isBiddingTasks {
    return pubType.value == 1;
  }

  // 用于控制表单的 验证等
  final formKey = GlobalKey<FormState>();

  // 发布类型 0 悬赏任务  1 招标任务
  var pubType = 0.obs;

  // 任务类型
  var taskType = "".obs;

  // 标题
  var title = "".obs;

  // 任务描述
  var desc = "".obs;

  // 展示截至时间
  var endTime = "".obs;

  // 提交时间
  var submitTime = "".obs;

  // 审核时间
  var auditTime = "".obs;

  // 星级
  var star = 1.obs;

  // 任务单价
  var price = "".obs;

  // 任务名额
  var quota = "".obs;

  // 预付金额
  var prepay = "".obs;

  // 输入框的控制器

  // 任务类型 控制器
  final taskTypeController = TextEditingController();

  // 标题 控制器
  final titleController = TextEditingController();

  // 任务描述 控制器
  final descController = TextEditingController();

  // 展示截至时间 控制器
  final endTimeController = TextEditingController();

  // 提交时间 控制器
  final submitController = TextEditingController();

  // 审核时间 控制器
  final auditController = TextEditingController();

  // 任务单价 控制器
  final priceController = TextEditingController();

  // 任务名额 控制器
  final quotaController = TextEditingController();

  // 预付金额 控制器
  final prepayController = TextEditingController();

  @override
  void onInit() {
    super.onInit();
  }

  @override
  void onReady() {
    super.onReady();
  }

  @override
  void onClose() {
    super.onClose();
  }

  // 打开类型选择
  openTypePick(context) async {
    if (Global.demandType.isEmpty) {
      AppToast.show("类目数据为空");
      return;
    }
    Pickers.showMultiLinkPicker(
      context,
      data: Global.demandType,
      pickerStyle: PickerStyle(
        textColor: const Color(0xff1A1A1A),
        textSize: 15.sp,
        backgroundColor: Colors.white,
        cancelButton: Container(
          alignment: Alignment.center,
          padding: const EdgeInsets.only(left: 22, right: 12),
          child: Text(
            '取消'.tr,
            style: TextStyle(
                color: Theme.of(context!).unselectedWidgetColor,
                fontSize: 16.0),
          ),
        ),
        commitButton: Container(
          alignment: Alignment.center,
          padding: const EdgeInsets.only(left: 12, right: 22),
          child: Text('确定'.tr,
              style: TextStyle(
                  color: Theme.of(context!).primaryColor, fontSize: 16.0)),
        ),
      ),
      columeNum: 3,
      suffix: [
        '',
        '',
        '',
      ],
      onConfirm: (p, b) {
        taskTypeController.text = p.map((e) => e.toString()).join('/');
        taskType.value = taskTypeController.text;
      },
    );
  }
}

publish_task_view.dart

 

import 'package:LS/common/index.dart';
import 'package:LS/components/condition_filter.dart';
import 'package:LS/routers/navigator.dart';
import 'package:LS/theme/index.dart';
import 'package:custom_radio_group_list/custom_radio_group_list.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../components/custom_app_bar.dart';
import 'publish_task_logic.dart';

class PublishTaskPage extends StatelessWidget {
  final logic = Get.find<PublishTaskLogic>();
  PublishTaskPage({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      top: false,
      child: Scaffold(
        backgroundColor: const Color(0xFFF2F4F7),
        body: SingleChildScrollView(
          child: Container(
            constraints: BoxConstraints(
              minHeight: 1.sh,
            ),
            decoration: const BoxDecoration(
              gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [
                    Color(0xFFFFD731),
                    Color.fromRGBO(246, 240, 189, 1),
                    Color(0xFFF2F4F7),
                  ],
                  stops: [
                    0,
                    0.3,
                    1
                  ]),
            ),
            child: Column(
              children: [
                const CustomAppBar(
                  title: '发布任务',
                ),
                Obx(
                  () => Form(
                    key: logic.formKey,
                    canPop: false,
                    onChanged: () {},
                    onPopInvoked: (i) {
                      print(i);
                    },
                    autovalidateMode: AutovalidateMode.onUserInteraction,
                    child: Column(
                      children: [
                        boardContainer(
                          "发布类型",
                          [selectInput()],
                        ),
                        boardContainer(
                          "任务类型",
                          [
                            iptOneLine(
                              "简单帮忙 | 账户注册",
                              readOnly: true,
                              controller: logic.taskTypeController,
                              onTap: () {
                                logic.openTypePick(context);
                              },
                              validator: (value) {
                                if (value == null || value.isEmpty) {
                                  return '任务类型不为空';
                                }
                                return null;
                              },
                            ),
                            SizedBox(height: 18.w),
                            formTitleLabel("标题"),
                            iptOneLine(
                              "项目核心短标题(10字以内)",
                              controller: logic.titleController,
                              onChanged: (s) {
                                logic.titleController.text = s;
                                logic.title.value = s;
                              },
                              validator: (value) {
                                if (value == null || value.isEmpty) {
                                  return '标题不为空';
                                }
                                if (value.length > 10) {
                                  return '标题必须10字以内';
                                }
                                return null;
                              },
                            ),
                            SizedBox(height: 18.w),
                            formTitleLabel("任务描述"),
                            iptOneLine(
                              "关键内容简要说明(15000字以内)",
                              maxLines: 4,
                              controller: logic.descController,
                              onChanged: (s) {
                                logic.descController.text = s;
                                logic.desc.value = s;
                              },
                              validator: (value) {
                                if (value == null || value.isEmpty) {
                                  return '任务描述不为空';
                                }
                                if (value.length > 15000) {
                                  return '任务描述不能超过15000字';
                                }
                                return null;
                              },
                            ),
                          ],
                        ),
                        boardContainer("展示截止时间", [
                          iptOneLine(
                            "最低展示30天",
                            controller: logic.endTimeController,
                            onChanged: (s) {
                              logic.endTimeController.text = s;
                              logic.endTime.value = s;
                            },
                            validator: (value) {
                              if (value == null || value.isEmpty) {
                                return '展示截止时间不为空';
                              }
                              if (!value.isNum) {
                                return '输入必须是数字';
                              }
                              if (value.isNum && int.parse(value) < 1) {
                                return '最低时间为1天';
                              }
                              return null;
                            },
                          ),
                          SizedBox(height: 17.5.w),
                          formTitleLabel("提交时间"),
                          iptOneLine(
                            "限时1小时内提交",
                            controller: logic.submitController,
                            onChanged: (s) {
                              logic.submitController.text = s;
                              logic.submitTime.value = s;
                            },
                            validator: (value) {
                              if (value == null || value.isEmpty) {
                                return '提交时间不为空';
                              }
                              if (!value.isNum) {
                                return '输入必须是数字';
                              }
                              if (value.isNum && int.parse(value) < 1) {
                                return '最低时间为1小时';
                              }
                              return null;
                            },
                          ),
                          SizedBox(height: 17.5.w),
                          formTitleLabel("审核时间"),
                          iptOneLine(
                            "限时1天内提交",
                            controller: logic.auditController,
                            onChanged: (s) {
                              logic.auditController.text = s;
                              logic.auditTime.value = s;
                            },
                            validator: (value) {
                              if (value == null || value.isEmpty) {
                                return '审核时间不为空';
                              }
                              if (!value.isNum) {
                                return '输入必须是数字';
                              }
                              if (value.isNum && int.parse(value) < 1) {
                                return '最低时间为1天';
                              }
                              return null;
                            },
                          ),
                          SizedBox(height: 12.w),
                          formTip("若未按时审核,将自动通过"),
                          SizedBox(height: 18.w),
                          formTitleLabel("星级"),
                          starSelectIpt(),
                          Visibility(
                            visible: !logic.isBiddingTasks,
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                SizedBox(height: 12.w),
                                formTitleLabel("做单次数"),
                                SizedBox(height: 12.w),
                                radioGroupFormField()
                              ],
                            ),
                          ),
                        ]),
                        boardContainer(
                          "任务单价",
                          [
                            iptOneLine(
                              "1拉手币起",
                              controller: logic.priceController,
                              onChanged: (s) {
                                logic.priceController.text = s;
                                logic.price.value = s;
                              },
                              validator: (value) {
                                if (value == null || value.isEmpty) {
                                  return '任务单价不为空';
                                }
                                if (!value.isNum) {
                                  return '输入必须是数字';
                                }
                                if (value.isNum && int.parse(value) < 1) {
                                  return '最低任务单价为1';
                                }
                                return null;
                              },
                            ),
                            SizedBox(height: 18.w),
                            formTitleLabel(
                                logic.isBiddingTasks ? "允许报名名额" : "任务名额"),
                            iptOneLine(
                              "最少1人",
                              controller: logic.quotaController,
                              onChanged: (s) {
                                logic.quotaController.text = s;
                                logic.quota.value = s;
                              },
                              validator: (value) {
                                if (value == null || value.isEmpty) {
                                  return '${logic.isBiddingTasks ? "允许报名名额" : "任务名额"}不为空';
                                }
                                if (!value.isNum) {
                                  return '输入必须是数字';
                                }
                                if (value.isNum && int.parse(value) < 1) {
                                  return '最低${logic.isBiddingTasks ? "允许报名名额" : "任务名额"}为1';
                                }
                                return null;
                              },
                            ),
                            SizedBox(height: 12.w),
                            Visibility(
                              visible: logic.isBiddingTasks,
                              child: formTip("最终只能确定1人接取任务"),
                            ),
                            SizedBox(height: 18.w),
                            formTitleLabel(
                                logic.isBiddingTasks ? "预付金额" : "预付金额"),
                            iptOneLine(
                              "0.00拉手币",
                              controller: logic.prepayController,
                              onChanged: (s) {
                                logic.prepayController.text = s;
                                logic.prepay.value = s;
                              },
                              validator: (value) {
                                if (value == null || value.isEmpty) {
                                  return '${logic.isBiddingTasks ? "预付金额" : "预付金额"}不为空';
                                }
                                if (!value.isNum) {
                                  return '输入必须是数字';
                                }
                                if (value.isNum && int.parse(value) < 1) {
                                  return '最低${logic.isBiddingTasks ? "预付金额" : "预付金额"}为1';
                                }
                                return null;
                              },
                            ),
                            SizedBox(height: 18.w),
                            formTip(logic.isBiddingTasks
                                ? "单价+手续费"
                                : "若未按时审核,将自动通过"),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
                PrimaryBtn(
                  text: "下一步(设置步骤)",
                  onTap: () {
                    bool isPass =
                        (logic.formKey.currentState as FormState).validate();
                    if (isPass) {
                      Nav.toGoAddStep();
                    }
                    // Nav.toGoAddStep();
                  },
                )
              ],
            ),
          ),
        ),
      ),
    );
  }

  // 单选框
  FormField<String> radioGroupFormField() {
    final stringList = ["仅限一次", "每日一次", "每人多次"];
    return FormField(
      builder: (FormFieldState<String> state) {
        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            SizedBox(
              height: 30.w,
              child: RadioGroup(
                items: stringList,
                onChanged: (value) {
                  print('Value : $value');
                  // selectedItem = value;
                  state.didChange(value);
                },
                scrollDirection: Axis.horizontal,
                selectedItem: state.value,
                shrinkWrap: false,
                activeColor: AppTheme.radioColor,
                // fillColor: AppTheme.radioColor,
                labelBuilder: (ctx, index) {
                  return Text(
                    stringList[index],
                    style: AppTheme.ts_15_500.copyWith(
                      color: AppTheme.fontPrimaryColor,
                    ),
                  );
                },
              ),
            ),
            // 添加验证逻辑
            if (state.hasError)
              Text(
                state.errorText ?? "",
                style: AppTheme.ts_12_500.copyWith(
                  color: Colors.red,
                ),
              ),
          ],
        );
      },
      validator: (value) {
        if (value == null) {
          return '请选择一个选项';
        }
        return null;
      },
    );
  }

  Container selectInput() {
    return Container(
      width: 355.w,
      decoration: BoxDecoration(
        color: AppTheme.iptBgColor,
        borderRadius: BorderRadius.circular(5.w),
      ),
      padding: EdgeInsets.symmetric(horizontal: 15.5.w, vertical: 12.w),
      child: ConditionFilter(
        isSelect: true,
        trigerWidget: Text(
          logic.isBiddingTasks ? "招标任务" : "悬赏任务",
          style: AppTheme.ts_14_500.copyWith(
            color: AppTheme.fontSelectColor,
          ),
        ),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            selectInputItem(
              "悬赏任务",
              () {
                logic.pubType.value = 0;
                Get.back();
              },
            ),
            Container(
              width: double.infinity,
              margin: EdgeInsets.symmetric(horizontal: 15.w),
              height: 1.w,
              color: Colors.black.withOpacity(0.05),
            ),
            selectInputItem(
              "招标任务",
              () {
                logic.pubType.value = 1;
                Get.back();
              },
            ),
          ],
        ),
      ),
    );
  }

  selectInputItem(String name, Function onTap) {
    return InkWell(
      onTap: () {
        onTap.call();
      },
      child: Container(
        margin: EdgeInsets.symmetric(vertical: 16.w),
        width: double.infinity,
        alignment: Alignment.center,
        child: Text(
          name,
          style: AppTheme.ts_13_500.copyWith(
            color: AppTheme.fontPrimaryColor,
          ),
        ),
      ),
    );
  }

  // 提示文本
  Text formTip(String tip) {
    return Text(
      tip,
      style: AppTheme.ts_12_500.copyWith(color: AppTheme.fontTipColor),
    );
  }

  // 星级选择弹窗
  Widget starSelectIpt() {
    return Container(
      alignment: Alignment.centerLeft,
      decoration: BoxDecoration(
        color: AppTheme.iptBgColor,
        borderRadius: BorderRadius.circular(5.w),
      ),
      width: 331.w,
      height: 45.w,
      padding: EdgeInsets.symmetric(
        horizontal: 15.5.w,
      ),
      child: StarSelectWidget(
        value: logic.star.value,
        onChange: (s) {
          logic.star.value = s;
          logic.update();
        },
      ),
    );
  }

  // 一行显示的输入框
  Container iptOneLine(
    String hintText, {
    bool readOnly = false,
    Function? onTap,
    int maxLines = 1,
    String? Function(String?)? validator,
    TextEditingController? controller,
    Function(String)? onChanged,
  }) {
    return Container(
      alignment: Alignment.centerLeft,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(5.w),
      ),
      clipBehavior: Clip.hardEdge,
      child: TextFormField(
        onTap: () {
          onTap?.call();
        },
        onChanged: onChanged,
        controller: controller,
        validator: validator,
        autovalidateMode: AutovalidateMode.onUserInteraction,
        maxLines: maxLines,
        minLines: maxLines,
        textAlignVertical: TextAlignVertical.center,
        readOnly: readOnly,
        decoration: InputDecoration(
          hintText: hintText.tr,
          counterText: "",
          hintStyle: AppTheme.ts_15_500.copyWith(
            color: AppTheme.iptPhColor,
          ),
          isDense: true,
          isCollapsed: false,
          border: InputBorder.none,
          contentPadding: EdgeInsets.symmetric(
            horizontal: 15.5.w,
            vertical: 10.w,
          ),
          filled: true,
          fillColor: AppTheme.iptBgColor,
          errorStyle: AppTheme.ts_12_500.copyWith(
            color: Colors.red,
          ),
        ),
        strutStyle: const StrutStyle(
          leading: 0,
          forceStrutHeight: true,
        ),
      ),
    );
  }

  // 内容面板
  Container boardContainer(String name, List<Widget> list) {
    return Container(
      width: 355.w,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(15.w),
      ),
      padding: EdgeInsets.only(
        top: 18.w,
        left: 12.w,
        bottom: 12.w,
        right: 12.w,
      ),
      margin: EdgeInsets.only(bottom: 10.w),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          formTitleLabel(name),
          ...list,
        ],
      ),
    );
  }

  Container formTitleLabel(String name) {
    return Container(
      margin: EdgeInsets.only(bottom: 10.w),
      child: Text(
        name,
        style: AppTheme.ts_15_700.copyWith(
          color: AppTheme.fontPrimaryColor,
        ),
      ),
    );
  }
}

相关推荐

  1. vue form验证

    2024-03-15 20:06:02       40 阅读
  2. Vue form验证

    2024-03-15 20:06:02       32 阅读
  3. 在css如何实现验证效果

    2024-03-15 20:06:02       62 阅读
  4. React-hook-form-mui(三):验证

    2024-03-15 20:06:02       48 阅读
  5. PHP 验证:邮件URL

    2024-03-15 20:06:02       23 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-15 20:06:02       101 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-15 20:06:02       109 阅读
  3. 在Django里面运行非项目文件

    2024-03-15 20:06:02       87 阅读
  4. Python语言-面向对象

    2024-03-15 20:06:02       96 阅读

热门阅读

  1. maya自动重定向测试

    2024-03-15 20:06:02       48 阅读
  2. 什么是虚假唤醒?为什么会产生虚假唤醒?

    2024-03-15 20:06:02       46 阅读
  3. python 之yaml库使用总结

    2024-03-15 20:06:02       42 阅读
  4. 详细说说JVM的class文件(一)

    2024-03-15 20:06:02       40 阅读
  5. JVM-4

    JVM-4

    2024-03-15 20:06:02      39 阅读
  6. 【测试知识】业务面试问答突击版1

    2024-03-15 20:06:02       35 阅读
  7. nginx应用

    2024-03-15 20:06:02       46 阅读
  8. C++中的C标准库、注释和条件编译

    2024-03-15 20:06:02       44 阅读
  9. Jenkins入门指南:自动化构建与部署的艺术

    2024-03-15 20:06:02       33 阅读