学习内容:
TextField(输入框,支持文本、密码类型)表单验证:判断输入是否为空、格式是否正确(如手机号)
实践任务:
做一个 “登录页面”:包含账号输入框、密码输入框(隐藏密码)、登录按钮
登录按钮点击时验证:账号 / 密码为空则提示 “请填写完整”
一、TextField组件详解※
1. 什么是TextField※
TextField是Flutter中用于接収用户文本输入的核心组件,支持单行/多行输入、密码隐藏、输入类型限制(如:数字、邮箱)等功能,是表单类页面(登录、注册、设置)的基础。
2. TextField核心属性※
| 属性名 | 作用 | 常用值示例 |
| controller | 控制输入框内容(获取/设置文本) | TextEditingController() |
| obscureText | 是否隐藏输入内容(密码框用) | true(隐藏)/false(显示,默认) |
| keyboardType | 键盘类型(控制弹出的键盘样式) | TextInputType.text(默认)、TextInputType.number(数字)、TextInputType.phone(手机号) |
| decoration | 输入框装饰(提示文字、边框、图标等) | InputDecoration(…) |
| hintText | 输入框为空时的提示文字 | “请输入账号” |
| labelText | 输入框的标签文字(可浮动) | “账号” |
| maxLines | 最大行数(单行输入用1,多行用null) | 1(默认,单行) |
| onChanged | 输入内容变化时的回调 | (value) => print("输入变化:$value") |
| enabled | 是否启用输入框(禁用时为灰色) | true(默认)/false(禁用) |
3. 基础用法:文本输入框(账号输入)※
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget{
App({super.key});
// 创建控制器,用于获取输入内容
final TextEditingController _usernameController = TextEditingController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('TextField 示例'),),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
// 账号输入框
TextField(
controller: _usernameController, // 绑定控制器
keyboardType: TextInputType.text, // 文本键盘
decoration: InputDecoration(
labelText: '账号', // 标签文字
hintText: '请输入账号', // 提示文字
prefixIcon: const Icon(Icons.person), // 左侧图标
border: const OutlineInputBorder(), // 带边框样式
),
// 输入变化时触发
onChanged: (value) {
print('账号输入变化:$value');
},
),
const SizedBox(height: 20,),
// 获取输入内容的按钮
ElevatedButton(
onPressed: () {
// 通过控制器获取输入内容
String username = _usernameController.text;
print('当前账号:$username');
},
child: const Text('获取账号'),
)
],
),
),
),
);
}
}- 运行效果

4. 密码输入框(隐藏输入内容)※
通过obscureText: true实现密码隐藏,配合keyboardType: TextInputType.visiblePassword弹出适合密码输入的键盘。
5. 控制器(TextEditingController)的重要性※
- 获取输入内容:通过controller.text实时获取输入框的文本。
- 设置默认值:初始化时设置controller.text = “默认值”,输入框会显示预设内容。
- 清空输入:调用controller.clear()可快速清空输入框。
注意:在StatefulWidget中使用时,需要在dispose方法中释放控制器,避免内存泄露。
@override void dispose() { _usernameController.dispose(); // 释放控制器 _passwordController.dispose(); super.dispose(); }
二、表单验证基础※
1. 为什么需要表单验证※
表单验证用于确保用户输入符合预期格式(如:手机号必须是11位数字、密码长度不少于6位),避免无效数据提交,提升应用健壮性。
2. 表单验证核心组件※
- Form:管理多个输入框的容器,提供整体验证功能。
- GlobalKey<FormState>:关联Form组件,用于触发验证和获取表单状态。
- validator:TextField的验证函数,用于返回错误提示文字(null表示验证通过)。
3. 基本验证流程※
- 创建GlobalKey<FormState>用于关联表单
- 用Form组件包裹所有TextField,并绑定key。
- 为每个TextField添加validator函数,定义验证规则。
- 点击按钮时,通过key.currentState!.validate()触发所有输入框的验证。
- 验证通过(返回true)则处理数据;否则显示错误提示。
4. 代码示例:简单表单验证※
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatefulWidget{
const App({super.key});
@override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
// 1. 创建表单key
final _formKey = GlobalKey<FormState>();
// 2. 创建控制器
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
// 3. 登录按钮点击事件
void _submitForm() {
// 触发所有输入框的验证
if (_formKey.currentState!.validate()) {
// 验证通过:获取输入内容并处理
String username = _usernameController.text;
String password = _passwordController.text;
print('登录信息:账号=$username,密码=$password。');
}
}
@override void dispose() {
_usernameController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('表单验证示例'),),
body: Padding(
padding: const EdgeInsets.all(20),
// 4. 用Form包裹输入框,绑定key
child: Form(
key: _formKey,
child: Column(
children: [
// 账号输入框
TextFormField( // Form中通常用TextFormField而不是TextField
controller: _usernameController,
decoration: InputDecoration(
labelText: '账号',
hintText: '请输入账号',
prefixIcon: const Icon(Icons.person),
border: const OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入账号'; // 验证失败,返回错误提示
}
return null;
},
),
const SizedBox(height: 20,),
// 密码输入框
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
labelText: '密码',
hintText: '请输入密码(不少于6位)',
prefixIcon: const Icon(Icons.lock),
border: const OutlineInputBorder()
),
// 密码验证规则:不能为空且长度不少于6位
validator: (value) {
if (value == null || value.isEmpty) {
return '密码不能为空';
}
if (value.length < 6) {
return '密码长度不能少于6位';
}
return null;
},
),
const SizedBox(height: 30,),
// 提交按钮
ElevatedButton(
onPressed: _submitForm,
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 50),
),
child: const Text('登录', style: TextStyle(fontSize: 18),),
)
],
),
),
),
),
);
}
}- 运行效果

5. 说明※
- TextFormField VS TextField:在Form中推荐使用TextFormField,它继承自TextField并增加了validator等表单相关属性。
- validator函数:参数value是输入框的内容(可空),返回null表示验证通过,返回字符串则显示该字符串作为错误提示。
- 错误提示样式:默认显示为红色小字,位于输入框下方,可通过InputDecoration.errorStyle自定义。
三、综合实战※
1. 任务需求:※
- 开发一个登录页面,包含:
- 账号输入框(TextFormField):带标签、提示文字、用户图标
- 密码输入框(TextFormField):带标签、提示文字、锁图标,隐藏输入内容
- 登录按钮(ElevatedButton):占满宽度,点击触发验证
- 验证逻辑
- 账号为空 → 提示“请输入账号”
- 密码为空 → 提示“请输入密码”
- 两者都为空 → 同时提示两个错误
- 验证通过 → 弹出对话框提示“登录成功”
2. 代码实现※
- 登录页代码(login_page.dart)
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
// 1. 创建表单key
final _formKey = GlobalKey<FormState>();
// 2. 创建控制器
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
// 3. 登录按钮点击事件
void _submitForm() {
// 触发所有输入框的验证
if (_formKey.currentState!.validate()) {
// 验证通过:获取输入内容并处理
String username = _usernameController.text;
String password = _passwordController.text;
// 显示登录成功对话框
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('登录成功'),
content: Text('账号:$username\n密码:$password'),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text('确定'))
],
),
);
}
}
@override void dispose() {
_usernameController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// 页面背景色
backgroundColor: Colors.grey[50],
body: Center(
child: SingleChildScrollView(
// 防止键盘弹出时内容溢出
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 应用图标
const Icon(
Icons.lock,
size: 80,
color: Colors.blue,
),
const SizedBox(height: 40,),
// 账号输入框
TextFormField(
controller: _usernameController,
keyboardType: TextInputType.text,
// 自动获取焦点(进入页面后自动弹出键盘)
autofocus: true,
decoration: InputDecoration(
labelText: '账号',
hintText: '请输入手机号',
prefixIcon: const Icon(Icons.person),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
// 聚焦时边框颜色
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.blue, width: 2),
borderRadius: BorderRadius.circular(10),
)
),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入账号'; // 验证失败,返回错误提示
}
return null;
},
),
const SizedBox(height: 20,),
// 密码输入框
TextFormField(
controller: _passwordController,
obscureText: true,
keyboardType: TextInputType.visiblePassword,
decoration: InputDecoration(
labelText: '密码',
hintText: '请输入密码(不少于6位)',
prefixIcon: const Icon(Icons.lock),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10))
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.blue, width: 2),
borderRadius: BorderRadius.circular(10)
)
),
// 密码验证规则:不能为空且长度不少于6位
validator: (value) {
if (value == null || value.isEmpty) {
return '密码不能为空';
}
if (value.length < 6) {
return '密码长度不能少于6位';
}
return null;
},
),
const SizedBox(height: 30,),
// 提交按钮
ElevatedButton(
onPressed: _submitForm,
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 50),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), // 圆角
)
),
child: const Text('登录', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),),
)
],
),
),
),
),
);
}
}入口文件(main.dart)
import 'pages/login_page.dart';
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget{
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '登录页面示例',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const LoginPage(),
);
}
}- 运行效果
