跳到主要内容

表单处理

表单是用户输入数据的重要方式。本章将介绍如何在 Flutter 中创建和验证表单。

基本表单

TextField

class BasicForm extends StatefulWidget {
@override
_BasicFormState createState() => _BasicFormState();
}

class _BasicFormState extends State<BasicForm> {
final _nameController = TextEditingController();
final _emailController = TextEditingController();

@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _nameController,
decoration: InputDecoration(
labelText: '姓名',
hintText: '请输入姓名',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
TextField(
controller: _emailController,
decoration: InputDecoration(
labelText: '邮箱',
hintText: '请输入邮箱',
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
print('姓名:${_nameController.text}');
print('邮箱:${_emailController.text}');
},
child: Text('提交'),
),
],
);
}
}

Form Widget

使用 Form 和 TextFormField 可以更好地管理表单状态和验证:

class LoginForm extends StatefulWidget {
@override
_LoginFormState createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();

@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}

void _submitForm() {
if (_formKey.currentState!.validate()) {
// 表单验证通过
print('邮箱:${_emailController.text}');
print('密码:${_passwordController.text}');

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('登录成功!')),
);
}
}

@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: '邮箱',
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入邮箱';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return '请输入有效的邮箱地址';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
labelText: '密码',
prefixIcon: Icon(Icons.lock),
border: OutlineInputBorder(),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入密码';
}
if (value.length < 6) {
return '密码长度不能少于6位';
}
return null;
},
),
SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submitForm,
child: Text('登录'),
),
),
],
),
);
}
}

表单验证

内置验证器

TextFormField(
decoration: InputDecoration(labelText: '邮箱'),
validator: (value) {
if (value == null || value.isEmpty) {
return '此字段不能为空';
}
return null;
},
)

自定义验证器

class CustomValidators {
static String? required(String? value) {
if (value == null || value.isEmpty) {
return '此字段不能为空';
}
return null;
}

static String? Function(String?) minLength(int min) {
return (String? value) {
if (value == null || value.length < min) {
return '长度不能少于 $min 个字符';
}
return null;
};
}

static String? email(String? value) {
if (value == null || value.isEmpty) {
return '请输入邮箱';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return '请输入有效的邮箱地址';
}
return null;
}

static String? phone(String? value) {
if (value == null || value.isEmpty) {
return '请输入手机号';
}
if (!RegExp(r'^1[3-9]\d{9}$').hasMatch(value)) {
return '请输入有效的手机号';
}
return null;
}
}

// 使用
TextFormField(
decoration: InputDecoration(labelText: '邮箱'),
validator: CustomValidators.email,
)

// 组合验证器
String? validatePassword(String? value) {
if (value == null || value.isEmpty) {
return '请输入密码';
}
if (value.length < 8) {
return '密码长度不能少于8位';
}
if (!value.contains(RegExp(r'[A-Z]'))) {
return '密码必须包含大写字母';
}
if (!value.contains(RegExp(r'[0-9]'))) {
return '密码必须包含数字';
}
return null;
}

输入框类型

// 普通文本
TextField(
decoration: InputDecoration(labelText: '姓名'),
textInputAction: TextInputAction.next,
)

// 数字输入
TextField(
decoration: InputDecoration(labelText: '年龄'),
keyboardType: TextInputType.number,
)

// 电话输入
TextField(
decoration: InputDecoration(labelText: '电话'),
keyboardType: TextInputType.phone,
)

// 多行文本
TextField(
decoration: InputDecoration(labelText: '简介'),
maxLines: 3,
keyboardType: TextInputType.multiline,
)

// 密码输入
TextField(
decoration: InputDecoration(labelText: '密码'),
obscureText: true,
)

// 带清除按钮
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: '搜索',
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () => _controller.clear(),
),
),
)

下拉选择

class DropdownExample extends StatefulWidget {
@override
_DropdownExampleState createState() => _DropdownExampleState();
}

class _DropdownExampleState extends State<DropdownExample> {
String? _selectedCity;

final List<String> _cities = ['北京', '上海', '广州', '深圳'];

@override
Widget build(BuildContext context) {
return DropdownButtonFormField<String>(
value: _selectedCity,
decoration: InputDecoration(
labelText: '城市',
border: OutlineInputBorder(),
),
items: _cities.map((city) {
return DropdownMenuItem(
value: city,
child: Text(city),
);
}).toList(),
onChanged: (value) {
setState(() => _selectedCity = value);
},
validator: (value) {
if (value == null) {
return '请选择城市';
}
return null;
},
);
}
}

日期时间选择

class DateTimePickerExample extends StatefulWidget {
@override
_DateTimePickerExampleState createState() => _DateTimePickerExampleState();
}

class _DateTimePickerExampleState extends State<DateTimePickerExample> {
DateTime? _selectedDate;
TimeOfDay? _selectedTime;

Future<void> _selectDate() async {
final date = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);

if (date != null) {
setState(() => _selectedDate = date);
}
}

Future<void> _selectTime() async {
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);

if (time != null) {
setState(() => _selectedTime = time);
}
}

@override
Widget build(BuildContext context) {
return Column(
children: [
ListTile(
title: Text('日期'),
subtitle: Text(
_selectedDate != null
? '${_selectedDate!.year}-${_selectedDate!.month}-${_selectedDate!.day}'
: '请选择日期',
),
trailing: Icon(Icons.calendar_today),
onTap: _selectDate,
),
ListTile(
title: Text('时间'),
subtitle: Text(
_selectedTime != null
? '${_selectedTime!.hour}:${_selectedTime!.minute}'
: '请选择时间',
),
trailing: Icon(Icons.access_time),
onTap: _selectTime,
),
],
);
}
}

小结

本章我们学习了:

  1. 基本表单:TextField、TextEditingController
  2. Form Widget:表单状态管理
  3. 表单验证:内置验证器、自定义验证器
  4. 输入框类型:各种键盘类型和输入模式
  5. 下拉选择:DropdownButton
  6. 日期时间选择:DatePicker、TimePicker

练习

  1. 创建一个完整的注册表单,包含姓名、邮箱、密码、确认密码、手机号
  2. 实现一个动态表单,可以根据选择显示不同的输入字段
  3. 创建一个表单验证库,包含常用验证规则