在 Flutter 中,TextField
是最常用的文本输入控件,用于接收用户的文字输入(单行、多行、密码、数字等)。
常用属性
属性 | 说明 |
---|---|
controller |
TextEditingController 控制输入内容,可获取或设置文本 |
focusNode |
管理焦点,可以手动获取或释放焦点 |
decoration |
InputDecoration ,控制输入框样式(边框、提示文字、图标等) |
keyboardType |
键盘类型(如 TextInputType.text 、TextInputType.number 、TextInputType.emailAddress ) |
obscureText |
是否隐藏文本(密码输入框用) |
maxLength |
最大输入长度 |
maxLines |
最大行数,默认 1,设置为 null 或大于 1 可以多行输入 |
minLines |
最小行数 |
style |
文本样式,TextStyle |
textAlign |
文本对齐方式,TextAlign.left / center / right |
cursorColor |
光标颜色 |
cursorHeight |
光标高度 |
enabled |
是否可编辑 |
readOnly |
是否只读 |
onTap |
点击输入框触发 |
onEditingComplete |
编辑完成触发,不同于 onSubmitted |
inputFormatters |
格式化输入,比如只允许数字或限制长度 |
InputDecoration 常用属性
decoration
用于美化输入框:
属性 | 说明 |
---|---|
hintText |
占位提示文字 |
labelText |
标签文字,浮动在上方 |
helperText |
辅助文字 |
prefixIcon |
前缀图标 |
suffixIcon |
后缀图标,如清除按钮 |
border |
输入框边框样式,OutlineInputBorder / UnderlineInputBorder |
filled |
是否填充背景色 |
fillColor |
填充背景颜色 |
errorText |
错误提示文字 |
基础代码
import 'package:flutter/material.dart';
class InputPage extends StatefulWidget {
const InputPage({super.key});
@override
State<InputPage> createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
// 文本消息
String _message = "";
// 输入框控制器
final TextEditingController _controllerName = TextEditingController();
final TextEditingController _controllerPassword = TextEditingController();
// 管理焦点
FocusNode focusNodeName = FocusNode();
FocusNode focusNodePassword = FocusNode();
FocusScopeNode? focusScopeNode;
// 输入框 - 用户名
Widget _buildName() {
return TextField(
// 控制器
controller: _controllerName,
// 焦点
autofocus: true,
// 焦点管理
focusNode: focusNodeName,
// 输入框的样式
decoration: const InputDecoration(
// 输入框的标签文本
labelText: '用户名',
// 输入框的辅助提示文本
hintText: '请输入',
// 输入框的前缀图标
prefixIcon: Icon(Icons.person),
// 输入框的后缀图标
suffixIcon: Icon(Icons.edit),
// 输入框的边框样式
border: OutlineInputBorder(),
),
// 输入改变事件
onChanged: (String value) {
setState(() {
_message = value;
});
},
// 提交回车事件
onSubmitted: (String value) {
// 隐藏键盘
focusScopeNode ??= FocusScope.of(context);
// 请求焦点
focusScopeNode?.requestFocus(focusNodePassword);
},
);
}
// 输入框 - 密码
Widget _buildPassword() {
return TextField(
controller: _controllerPassword,
// 密码显示
obscureText: true,
// 焦点管理
focusNode: focusNodePassword,
// 输入框的样式
decoration: const InputDecoration(
labelText: '密码',
hintText: '请输入',
prefixIcon: Icon(Icons.person),
suffixIcon: Icon(Icons.edit),
border: OutlineInputBorder(),
),
);
}
// 按钮
Widget _buildButton() {
return ElevatedButton(
child: const Text('登录 Now!'),
onPressed: () {
setState(() {
_message =
'name:${_controllerName.text}, pass:${_controllerPassword.text}';
});
},
);
}
// 显示
Widget _buildMessage() {
return Text(_message);
}
@override
void dispose() {
// 释放控制器
_controllerName.dispose();
_controllerPassword.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('InputPage')),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
_buildName(),
const SizedBox(height: 10),
_buildPassword(),
const SizedBox(height: 10),
_buildButton(),
const SizedBox(height: 10),
_buildMessage(),
],
),
),
);
}
}

FocusNode的作用
综合代码
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(
title: 'FocusNode 示例',
theme: ThemeData(primarySwatch: Colors.blue),
home: const FocusDemoPage(),
);
}
}
class FocusDemoPage extends StatefulWidget {
const FocusDemoPage({super.key});
@override
State<FocusDemoPage> createState() => _FocusDemoPageState();
}
class _FocusDemoPageState extends State<FocusDemoPage> {
// 定义多个 FocusNode
final FocusNode _focusNode1 = FocusNode(); // 用于监听焦点变化
final FocusNode _focusNode2 = FocusNode(); // 主动获取/失去焦点
final FocusNode _usernameFocus = FocusNode(); // 多输入框切换
final FocusNode _passwordFocus = FocusNode();
final FocusNode _focusNode4 = FocusNode(); // 动态 UI 改变
@override
void initState() {
super.initState();
// 1️⃣ 监听焦点变化
// 在初始化时添加监听器
_focusNode1.addListener(() {
if (_focusNode1.hasFocus) {
debugPrint("输入框1 获得了焦点");
} else {
debugPrint("输入框1 失去了焦点");
}
});
// 4️⃣ 动态UI刷新
// 在初始化时添加监听器
_focusNode4.addListener(() {
setState(() {});
});
}
@override
void dispose() {
// 释放 FocusNode
// 如果不释放 FocusNode,可能会导致内存泄漏
_focusNode1.dispose();
_focusNode2.dispose();
_usernameFocus.dispose();
_passwordFocus.dispose();
_focusNode4.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("FocusNode 完整示例")),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 1️⃣ 监听焦点变化
const Text(
"1. 监听焦点变化",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
TextField(
focusNode: _focusNode1,
decoration: const InputDecoration(labelText: "输入框1(监听焦点变化)"),
),
const SizedBox(height: 20),
// 2️⃣ 主动控制焦点
const Text(
"2. 主动获取/失去焦点",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
TextField(
focusNode: _focusNode2,
decoration: const InputDecoration(labelText: "输入框2(按钮控制焦点)"),
),
Row(
children: [
ElevatedButton(
onPressed: () {
// 主动获取焦点:调用 requestFocus 方法
// FocusScope.of(context): 这是在获取context对应的 FocusScope
// requestFocus: 请求焦点
// 请求获取焦点之后会出现键盘
// 为什么不能用_focusNode2.requestFocus()?
// 因为 FocusNode 需要在其对应的 BuildContext 中使用
FocusScope.of(context).requestFocus(_focusNode2);
},
child: const Text("获取焦点"),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () {
// 主动失去焦点:调用 unfocus 方法
// 失去焦点之后键盘会收起
_focusNode2.unfocus();
},
child: const Text("失去焦点"),
),
],
),
const SizedBox(height: 20),
// 3️⃣ 多输入框切换
const Text(
"3. 多输入框切换焦点",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
TextField(
focusNode: _usernameFocus,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(labelText: "用户名"),
// 提交回车事件:
// _ 表示当前输入框的内容
onSubmitted: (_) {
// 请求焦点: 密码输入框
FocusScope.of(context).requestFocus(_passwordFocus);
},
),
TextField(
focusNode: _passwordFocus,
obscureText: true,
decoration: const InputDecoration(labelText: "密码"),
),
const SizedBox(height: 20),
// 4️⃣ 动态 UI 改变
const Text(
"4. 根据焦点状态动态改变UI",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
TextField(
focusNode: _focusNode4,
decoration: InputDecoration(
labelText: "输入框4(获取焦点时变色)",
border: const OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: _focusNode4.hasFocus ? Colors.red : Colors.grey,
width: 2,
),
),
),
),
],
),
),
);
}
}

主要作用:
是否获取焦点
检测输入框是否获取焦点
_focusNode1.addListener(() {
if (_focusNode1.hasFocus) {
debugPrint("输入框1 获得了焦点");
} else {
debugPrint("输入框1 失去了焦点");
}
});
TextField(
focusNode: _focusNode1, // 绑定_focusNode1
decoration: const InputDecoration(labelText: "输入框1(监听焦点变化)"),
),
手动获取/释放焦点
手动获取/释放焦点:有时候我们需要在点击按钮时,让某个输入框自动弹出键盘,或者收起键盘。
TextField(
focusNode: _focusNode2,
decoration: const InputDecoration(labelText: "输入框2(按钮控制焦点)"),
),
Row(
children: [
ElevatedButton(
onPressed: () {
// 主动获取焦点:调用 requestFocus 方法
// FocusScope.of(context): 这是在获取context对应的 FocusScope
// requestFocus: 请求焦点
// 请求获取焦点之后会出现键盘
// 为什么不能用_focusNode2.requestFocus()?
// 因为 FocusNode 需要在其对应的 BuildContext 中使用
FocusScope.of(context).requestFocus(_focusNode2);
},
child: const Text("获取焦点"),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () {
// 主动失去焦点:调用 unfocus 方法
// 失去焦点之后键盘会收起
_focusNode2.unfocus();
},
child: const Text("失去焦点"),
),
],
),
焦点切换
在多个输入框间切换焦点:比如做表单时,用户输入完“用户名”后,点击下一步会自动跳到“密码”输入框。
TextField(
focusNode: _usernameFocus,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(labelText: "用户名"),
// 提交回车事件:
// _ 表示当前输入框的内容
onSubmitted: (_) {
// 请求焦点: 密码输入框
FocusScope.of(context).requestFocus(_passwordFocus);
},
),
TextField(
focusNode: _passwordFocus,
obscureText: true,
decoration: const InputDecoration(labelText: "密码"),
),
结合 UI 做效果
结合 UI 做效果:可以根据焦点状态来动态改变输入框样式(比如获取焦点时边框变蓝色)。
TextField(
focusNode: _focusNode4,
decoration: InputDecoration(
labelText: "输入框4(获取焦点时变色)",
border: const OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: _focusNode4.hasFocus ? Colors.red : Colors.grey,
width: 2,
),
),
),
),
TextEditingController的作用
综合代码
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(
title: 'TextEditingController Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const TextFieldDemoPage(),
);
}
}
class TextFieldDemoPage extends StatefulWidget {
const TextFieldDemoPage({super.key});
// createState()方法 在构建TextFieldDemoPage之后就执行
@override
State<TextFieldDemoPage> createState() => _TextFieldDemoPageState();
}
class _TextFieldDemoPageState extends State<TextFieldDemoPage> {
// 1. 创建控制器,并设置初始值
final TextEditingController _controller = TextEditingController(
text: "Hello Flutter",
);
@override
void initState() {
super.initState();
// 2. 监听输入框内容变化
_controller.addListener(() {
print("输入内容变化:${_controller.text}");
});
}
@override
void dispose() {
_controller.dispose(); // 避免内存泄漏
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("TextEditingController Demo")),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
TextField(
controller: _controller, // 绑定控制器
decoration: const InputDecoration(
labelText: "请输入内容",
border: OutlineInputBorder(),
),
),
const SizedBox(height: 20),
// 读取输入内容
ElevatedButton(
onPressed: () {
print("当前输入内容:${_controller.text}");
},
child: const Text("读取内容"),
),
// 修改输入内容
ElevatedButton(
onPressed: () {
_controller.text = "新内容设置成功!";
},
child: const Text("修改内容"),
),
// 控制光标和选中范围
ElevatedButton(
onPressed: () {
_controller.selection = TextSelection(
baseOffset: 0, // 选中范围的起始位置
extentOffset: _controller.text.length, // 选中范围的结束位置
);
},
child: const Text("全选文本"),
),
// 清空输入框
ElevatedButton(
onPressed: () {
_controller.clear();
},
child: const Text("清空内容"),
),
],
),
),
);
}
}

主要作用
获取输入内容
获取输入内容:可以通过 controller.text
获取用户在输入框中输入的内容。
// 读取输入内容
ElevatedButton(
onPressed: () {
print("当前输入内容:${_controller.text}");
},
child: const Text("读取内容"),
),
设置输入内容
设置输入内容:可以主动给输入框赋值,常用于表单初始化或清空输入框。
// 1. 创建控制器,并设置初始值
final TextEditingController _controller = TextEditingController(
text: "Hello Flutter",
);
controller: _controller, // 绑定控制器
// 清空输入框
ElevatedButton(
onPressed: () {
_controller.clear();
},
child: const Text("清空内容"),
),
监听输入变化
监听输入变化:通过 addListener
方法可以实时监听输入框内容变化,而不需要依赖 onChanged
回调。
// 2. 监听输入框内容变化
_controller.addListener(() {
print("输入内容变化:${_controller.text}");
});
}
控制光标位置
控制光标位置 / 选择文本:TextEditingController
还能操作光标和选中文本范围。
// 控制光标和选中范围
ElevatedButton(
onPressed: () {
_controller.selection = TextSelection(
baseOffset: 0, // 选中范围的起始位置
extentOffset: _controller.text.length, // 选中范围的结束位置
);
},
child: const Text("全选文本"),
),
// 光标移动到末尾
_controller.selection = TextSelection.fromPosition(
TextPosition(offset: _controller.text.length),
);