在大多数应用中,我们不仅需要向用户展示信息,还需要获取用户的输入。Flutter 提供了强大的 TextField
组件来处理文本输入,并通过 Form
组件来对多个输入进行分组、验证和管理。
学习目标
- 掌握
TextField
的基本用法,并学会使用TextEditingController
来获取和控制输入内容。 - 学会使用
InputDecoration
来美化输入框,添加标签、提示、图标和边框。 - 理解
Form
Widget 的作用,并学会使用GlobalKey
来管理表单状态。 - 掌握
TextFormField
,学会如何编写validator
函数来进行输入验证。
1. TextField
:获取单行输入
TextField
是一个允许用户通过硬件键盘或屏幕键盘输入文本的组件。要有效地使用它,我们必须学会如何获取 和监听其内容。
核心:TextEditingController
获取 TextField
内容最常用和推荐的方式是使用 TextEditingController
。它是一个可监听的对象,你可以用它来:
- 读取
TextField
中的当前文本 (controller.text
)。 - 在代码中设置
TextField
的文本 (controller.text = 'new value'
)。 - 监听文本的变化 (
controller.addListener(...)
)。
重要 :TextEditingController
会持有状态,因此它必须在 StatefulWidget
的 State
对象中被创建和管理。并且,为了避免内存泄漏,在 dispose
方法中销毁它是一个好习惯。
美化输入框:decoration
TextField
的 decoration
属性接收一个 InputDecoration
对象,它能讓你定制输入框的几乎所有视觉方面。
- 常用属性 :
labelText
: 当输入框为空且未聚焦时显示的浮动标签。hintText
: 当输入框为空时显示的灰色提示文字。helperText
: 在输入框下方显示的小段帮助文字。errorText
: 在输入框下方显示的红色错误信息(通常用于表单验证)。icon
,prefixIcon
,suffixIcon
: 在输入框外部、内部前方、内部后方添加图标。border
: 定义输入框的边框样式,常用OutlineInputBorder()
(四周边框) 或UnderlineInputBorder()
(下划线边框)。
代码示例:一个简单的登录输入框
java
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
appBar: Text('TextField Demo'),
body: Padding(
padding: EdgeInsets.all(20.0),
child: SimpleInputForm(),
),
),
);
}
}
class SimpleInputForm extends StatefulWidget {
const SimpleInputForm({super.key});
@override
State<SimpleInputForm> createState() => _SimpleInputFormState();
}
class _SimpleInputFormState extends State<SimpleInputForm> {
// 1. 创建 TextEditingController
late final TextEditingController _usernameController;
String _displayText = '...';
@override
void initState() {
super.initState();
_usernameController = TextEditingController();
}
@override
void dispose() {
// 3. 在 widget 销毁时,dispose controller
_usernameController.dispose();
super.dispose();
}
void _showUsername() {
setState(() {
// 4. 通过 controller.text 获取值
_displayText = _usernameController.text;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _usernameController, // 2. 绑定 controller
decoration: InputDecoration(
labelText: '用户名',
hintText: '请输入您的用户名',
prefixIcon: const Icon(Icons.person),
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 20),
TextField(
obscureText: true, // 设置为密码输入框
decoration: InputDecoration(
labelText: '密码',
prefixIcon: const Icon(Icons.lock),
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: _showUsername,
child: const Text('获取用户名'),
),
const SizedBox(height: 20),
Text('您输入了: $_displayText'),
],
);
}
}
2. Form
和 TextFormField
:处理复杂表单
当你有多个输入框,并且需要对它们进行统一的验证 (Validation) 和提交 (Submission) 时,就应该使用 Form
Widget。
Form
: 作为一个容器,它可以追踪其内部所有FormField
的状态。TextFormField
:TextField
的一个特殊版本,它天生就能与Form
协同工作。它比TextField
增加了一个关键属性:validator
。GlobalKey<FormState>
: 这是操作Form
的钥匙。通过这个key
,我们可以调用Form
的方法,如validate()
和save()
。
验证流程
- 创建一个
GlobalKey<FormState>
并将其赋给Form
Widget的key
属性。 - 在
TextFormField
中提供一个validator
函数。这个函数接收当前输入框的值,如果值有效,返回null
;如果无效,返回一个描述错误的字符串。 - 当用户尝试提交时(例如点击"登录"按钮),调用
_formKey.currentState!.validate()
。 - 这会触发
Form
内部所有TextFormField
的validator
函数。- 如果所有
validator
都返回null
,validate()
方法返回true
。 - 只要有一个
validator
返回了错误字符串,validate()
方法就会返回false
,并且该错误字符串会自动显示在对应的TextFormField
下方。
- 如果所有
代码示例:一个带验证的登录表单
这个例子展示了如何使用 Form
、TextFormField
和 GlobalKey
来构建一个带有输入验证功能的完整登录表单。
php
import 'package.flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Form Validation Demo')),
body: const Padding(
padding: EdgeInsets.all(24.0),
child: LoginForm(),
),
),
);
}
}
class LoginForm extends StatefulWidget {
const LoginForm({super.key});
@override
State<LoginForm> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
// 1. 创建 GlobalKey
final _formKey = GlobalKey<FormState>();
void _submitForm() {
// 3. 调用 key 的 validate 方法
if (_formKey.currentState!.validate()) {
// 如果验证通过,则显示一个 Snackbar
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('处理数据...')),
);
// 在这里可以执行登录逻辑,比如调用 API
}
}
@override
Widget build(BuildContext context) {
// 2. 用 Form Widget 包裹你的表单字段
return Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
labelText: '邮箱',
hintText: '请输入您的邮箱地址',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
// 验证逻辑
validator: (value) {
if (value == null || value.isEmpty) {
return '邮箱不能为空';
}
if (!value.contains('@')) {
return '请输入有效的邮箱地址';
}
return null; // 返回 null 表示验证通过
},
),
const SizedBox(height: 20),
TextFormField(
decoration: const InputDecoration(
labelText: '密码',
hintText: '请输入您的密码',
border: OutlineInputBorder(),
),
obscureText: true,
// 验证逻辑
validator: (value) {
if (value == null || value.isEmpty) {
return '密码不能为空';
}
if (value.length < 6) {
return '密码长度不能少于6位';
}
return null; // 返回 null 表示验证通过
},
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: _submitForm,
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50), // 设置按钮高度
),
child: const Text('登录', style: TextStyle(fontSize: 18)),
),
],
),
);
}
}
总结
至此,我们已经掌握了 Flutter 中处理用户输入的两大核心利器。
- 对于简单的、单个的输入 ,使用
TextField
配合TextEditingController
就足够了。 - 对于需要多个输入字段、统一验证和管理的场景 (如登录、注册),则必须使用
Form
、TextFormField
和GlobalKey<FormState>
` 的黄金组合。
现在,你已经具备了构建具有完整信息展示和用户交互功能页面的核心能力。在接下来的教程中,我们将学习如何展示列表和网格数据,这是构建信息流、商品列表等页面的关键。我们下篇见!