原因
公司需求开发app项目,可是没有多余的flutter工程师,只能让咱们的前端组去做flutter,根据java的开发经历快速的上手dart,可是一些开发思路仍是偏前端,所以写了一套偏向于前端的flutter表单计划。
规划思路
给予之前写的element-plus的项目的表单经历,我将整个表单分成了两部分一个是用于承载组件的FormItem,另外一部分则是组件本身,给予规划咱们需求支撑的组件目前有输入框、多行文本、挑选框、tag挑选框、头像上传、开关等组件,这儿咱们只介绍一下输入框和挑选框的规划思路,其他的组件规划是类似的。
承载组件的FormItem的规划
首要这儿咱们是需求支撑用户传递他需求的字段的即label,然后关于布局方法咱们也需求支撑,在移动端有两种常见的布局方法关于表单项。
第二步item要支撑对不同类型组件的烘托,这儿咱们能够用枚举来解决,比如咱们支撑inupt,select这两种类型。
第三步就是根据不同组件类型来烘托不同的组件。
// ignore_for_file: curly_braces_in_flow_control_structures
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ventora/components/form/input_text.dart';
import 'package:ventora/components/form/select.dart';
enum FormType { input, select }
class FormItem<T> extends StatelessWidget {
const FormItem(
{super.key,
required this.type,
this.lable,
this.value,
this.setValue,
this.backgroundColor,
this.lableColor,
this.buildItem,
this.lableWidth,
this.rowCrossAxisAlignment,
this.margin,
this.hintText,
this.selectList,
this.keyboardType,
this.valueList,
this.onClick,
this.padding,
this.isSameLine = true,
this.controller,
this.maxLength,
this.textHeight,});
final FormType type;
final String? lable;
final T? value;
final Function? setValue;
final Color? backgroundColor;
final Color? lableColor;
final Widget? Function()? buildItem;
final double? lableWidth;
final CrossAxisAlignment? rowCrossAxisAlignment;
final EdgeInsetsGeometry? margin;
final List<String>? selectList;
final String? hintText;
final TextInputType? keyboardType;
final Function? onClick;
final List<String>? itemList;
final bool isSameLine;
final EdgeInsets? padding;
final TextEditingController? controller;
final int? maxLength;
final double? textHeight;
final double? size;
Widget initExpanded(Widget child) => isSameLine ? Expanded(child: child) : child;
_formBuild() {
if (type == FormType.input)
return Expanded(
child: InputText(
setValue: setValue as Function(String)?,
controller: controller ?? TextEditingController(text: (value as Rx<String>).value),
hintText: hintText,
textHeight: textHeight,
enabled:enabled,
contentPadding:contentPadding,
keyboardType: keyboardType));
if (type == FormType.select) return Expanded(child: Select(value: value as String, setValue: setValue as Function, selectList: selectList!));
}
List<Widget> _listBuild() {
return [
if (lable != null)
Container(
width: lableWidth ?? 88,
alignment: Alignment.topLeft,
margin: margin ?? EdgeInsets.zero,
child: Text(lable!,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: lableColor ?? Colors.white,
)),
),
_formBuild()
];
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: padding ?? const EdgeInsets.only(top: 12, bottom: 12, left: 16, right: 16),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
color: backgroundColor ?? ColorsLandon.backgroundHome,
),
child: isSameLine
? Row(
crossAxisAlignment: rowCrossAxisAlignment ?? CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _listBuild(),
)
: Column(
crossAxisAlignment: rowCrossAxisAlignment ?? CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _listBuild(),
),
);
}
}
规划Input组件的编写
在vue里边咱们是能够给input组件传递一个双向绑定的值,可是这件事在flutter中似乎欠好完成,所以这儿咱们能够通过TextEditingController的方法绕一下完成双向绑定计划。
import 'package:flutter/material.dart';
import 'package:ventora/styles/colors_lando.dart';
class InputText<T> extends StatelessWidget {
final Function(String)? setValue;
final String? hintText;
final TextInputType? keyboardType;
final TextEditingController controller;
final double? textHeight;
final EdgeInsetsGeometry? contentPadding;
final bool? enabled;
const InputText(
{super.key, required this.setValue, this.hintText, this.keyboardType, required this.controller, this.textHeight, this.contentPadding, this.enabled});
@override
Widget build(BuildContext context) {
return SizedBox(
height: textHeight ?? 15.0 * 1.5,
width: double.infinity,
child: TextField(
enabled: enabled ?? true,
style: TextStyle(color: enabled != false ? ColorsLandon.initColors : ColorsLandon.formHintColor, fontSize: 15, fontWeight: FontWeight.w600),
cursorColor: ColorsLandon.initColors,
cursorHeight: textHeight ?? 15.0 * 1.5,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: contentPadding ?? const EdgeInsets.only(bottom: 10),
hintText: hintText,
hintStyle: const TextStyle(color: ColorsLandon.hintInitColors, fontSize: 15),
),
controller: controller,
onChanged: setValue,
keyboardType: keyboardType ?? TextInputType.text),
);
}
}
规划select组件
首要给予移动端的交互逻辑咱们能够参考一下小程序或者vant的ui逻辑能够规划成点击出现一个抽屉让用户挑选。
已然规划定下来了咱们能够写一下完成
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ventora/styles/colors_lando.dart';
import 'package:ventora/styles/text_style_lando.dart';
class Select extends StatelessWidget {
final String value;
final Function setValue;
final String? hintText;
final List<String> selectList;
final TextEditingController _controller = TextEditingController();
Select({super.key, required this.value, required this.setValue, this.hintText, required this.selectList});
@override
Widget build(BuildContext context) {
_controller.text = value;
return GestureDetector(
onTap: () => {
showModalBottomSheet(
context: context,
builder: (context) => Container(
color: ColorsLandon.backgroundColor,
width: double.infinity,
padding: const EdgeInsets.only(bottom: 38, top: 20, left: 20, right: 20),
child: Column(mainAxisSize: MainAxisSize.min, children: [
...selectList
.map((item) => GestureDetector(
onTap: () {
setValue(item);
Navigator.pop(context);
},
child: Container(
width: double.infinity,
margin: const EdgeInsets.only(bottom: 16),
decoration: const BoxDecoration(color: ColorsLandon.hintBackground, borderRadius: BorderRadius.all(Radius.circular(12))),
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
item,
style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600),
),
if (item == value) const Icon(Icons.done, color: ColorsLandon.initColors)
],
))))
.toList(),
const SizedBox(
height: 20,
)
]),
))
},
child: Container(
height: 18.0,
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
value,
style: TextStyleLando.formItemText,
),
const Icon(
Icons.expand_more,
color: Colors.white,
size: 16,
)
],
)));
}
}
成果
其实整个form可贵当地在于规划思路不同的组件能够用flutter自带的组件来完成即可,主要是咱们需求先规划formItem怎么怎么规划会对使用者更加友爱,使用作用
FormItem(
type: FormType.text,
label: 'Name',
controller: logic.iForm.nickname,
hintText: '4~30 characters',
FormItem(
type: FormType.select,
label: 'Gender',
setValue: (value) => {
logic.setValue(logic.iForm.gender, value),
},
value: logic.iForm.gender.value,
selectList: const [' Male', ' Female', '⚧ Non-binary'],
)),
竣工下机!