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,
),
),
);
}
}