Flutter基础组件(3):TextField(输入框)
TextField
是一个允许用户输入文本的小部件。它非常灵活,支持多种文本输入场景,如单行文本、多行文本、密码输入、数值输入等。TextField
还提供了丰富的定制选项,包括文本样式、图标、控制器等。
一、基础用法
TextField
最基本的用法是创建一个可以输入单行文本的字段:
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter your name',
),
)
这将创建一个带有标签和下划线的文本输入框。
二、常用属性
下面是一些常用的TextField
属性以及它们的说明:
controller
:控制输入框的文本内容,可以通过TextEditingController
进行管理。decoration
:输入框的装饰,可以定义输入框的边框、背景、提示文本等样式。- icon:设置左边显示的图标
- labelText:在输入框上面显示一个提示的文本
- hintText:显示提示的占位文字
- border:输入框的边框,默认底部有一个边框,可以通过 InputBorder.none 删除掉
- filled:是否填充输入框,默认为false
- fillColor:输入框填充的颜色
obscureText
:是否将输入内容隐藏,常用于密码输入框。enabled
:输入框是否可编辑。maxLength
:允许输入的最大字符数。textInputAction
:键盘操作按钮的类型,例如完成、继续等。keyboardType
:键盘的类型,如文本、数字、URL等。onChanged
:文本变化时的回调函数。onSubmitted
:用户提交输入时的回调函数。focusNode
:用于控制输入框的焦点获取和失去。autofocus
:是否自动获取焦点。style
:输入框文本的样式,如字体大小、颜色等。
2.1 文本输入类型
TextField
支持多种文本输入类型,通过TextInputType
属性设置:
文本输入
TextField(
keyboardType: TextInputType.text,
// ... 其他属性
)
数字输入
TextField(
keyboardType: TextInputType.number,
// ... 其他属性
)
多行文本
TextField(
keyboardType: TextInputType.multiline,
maxLines: null, // 允许无限行
// ... 其他属性
)
密码输入
TextField(
obscureText: true, // 隐藏输入的文本
// ... 其他属性
)
obscureText
属性用于指定输入框的文本是否应该被隐藏,常用于密码输入框。当设置为true
时,输入的文本将以圆点或其他隐藏字符显示。
2.2 控制器和焦点
TextField
可以使用TextEditingController
控制输入的文本,以及通过使用FocusNode
监听焦点的改变事件:
TextEditingController _controller = TextEditingController(); // 创建控制器
FocusNode _focusNode = FocusNode(); // _focusNode绑定输入框
TextField(
controller: _controller,
focusNode: _focusNode,
// 监听焦点变化
_focusNode.addListener((){
print(focusNode.hasFocus);
});
)
获得焦点时focusNode.hasFocus
值为true
,失去焦点时为false
。
2.3 输入装饰
InputDecoration
是一个用于定义TextField
外观的类,包括标签、占位符文本、前缀/后缀图标、错误文本等:
TextField(
decoration: InputDecoration(
prefixIcon: Icon(Icons.person),
suffixIcon: Icon(Icons.clear),
hintText: 'Enter your name',
errorText: 'This field is required',
contentPadding: EdgeInsets.all(8.0),
border: OutlineInputBorder(),
),
// ... 其他属性
)
2.4 输入验证
TextField
支持输入验证,通过TextInputFormatter
或者validator
属性:
TextField(
inputFormatters: [
WhitelistingTextInputFormatter.digitsOnly, // 只允许输入数字
],
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your name';
}
return null;
},
// ... 其他属性
)
2.5 自动完成和填充
TextField
支持自动完成和自动填充功能,通过AutofillHints
:
TextField(
autofillHints: [AutofillHints.name],
// ... 其他属性
)
三、获取输入内容
获取输入内容有两种方式:
- 定义两个变量,用于保存用户名和密码,然后在
onChange
触发时,各自保存一下输入内容。 - 通过
controller
直接获取。
第一种方式比较简单,不再举例,我们来重点看一下第二种方式,我们以用户名输入框举例:
定义一个controller
:
// 定义一个controller
TextEditingController _unameController = TextEditingController();
然后设置输入框 controller:
TextField(
autofocus: true,
controller: _unameController, // 设置controller
// ...
)
通过 controller 获取输入框内容
debugPrint(_unameController.text)
四、监听文本输入完成和提交
你可以监听文本输入完成和提交的回调,例如,当用户输入文本时执行一些操作:
TextField(
onEditingComplete: () {
// 文本输入完成时的回调
},
onSubmitted: (value) {
// 文本提交时的回调
},
// ... 其他属性
)
五、监听文本变化
监听文本变化也有两种方式:
- 设置
onChange
回调,如:
TextField(
autofocus: true,
onChanged: (value) {
print("onChange: $value");
}
)
- 通过
controller
监听,如:
@override
void initState() {
// 监听输入改变
_unameController.addListener((){
print(_unameController.text);
});
}
两种方式相比,onChanged
是专门用于监听文本变化,而controller
的功能却多一些,除了能监听文本变化外,它还可以设置默认值、选择文本,下面我们看一个例子:
示例代码如下:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[TextFieldDemo()],
),
);
}
}
class TextFieldDemo extends StatefulWidget {
@override
_TextFieldDemoState createState() => _TextFieldDemoState();
}
class _TextFieldDemoState extends State<TextFieldDemo> {
@override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "username",
hintText: "请输入用户名",
border: InputBorder.none,
filled: true,
fillColor: Colors.lightGreen),
onChanged: (value) {
print("onChanged:$value");
},
onSubmitted: (value) {
print("onSubmitted:$value");
},
);
}
}
运行效果如下图所示:
六、自定义样式
虽然我们可以通过decoration
属性来定义输入框样式,下面以自定义输入框下划线颜色为例来介绍一下:
TextField(
decoration: InputDecoration(
labelText: "请输入用户名",
prefixIcon: Icon(Icons.person),
// 未获得焦点下划线设为灰色
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
//获得焦点下划线设为蓝色
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
),
),
),
上面代码我们直接通过InputDecoration
的enabledBorder
和focusedBorder
来分别设置了输入框在未获取焦点和获得焦点后的下划线颜色。另外,我们也可以通过主题来自定义输入框的样式,下面我们探索一下如何在不使用enabledBorder
和focusedBorder
的情况下来自定义下滑线颜色。
由于TextField
在绘制下划线时使用的颜色是主题色里面的hintColor
,但提示文本颜色也是用的hintColor
, 如果我们直接修改hintColor
,那么下划线和提示文本的颜色都会变。值得高兴的是decoration
中可以设置hintStyle
,它可以覆盖hintColor
,并且主题中可以通过inputDecorationTheme
来设置输入框默认的decoration
。所以我们可以通过主题来自定义,代码如下:
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(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: MyHomeBody(),
)
);
}
}
class MyHomeBody extends StatelessWidget {
const MyHomeBody({super.key});
@override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
hintColor: Colors.grey[200], //定义下划线颜色
inputDecorationTheme: const InputDecorationTheme(
labelStyle: TextStyle(color: Colors.grey), //定义label字体样式
hintStyle:
TextStyle(color: Colors.grey, fontSize: 14.0) //定义提示文本样式
)),
child: const Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
labelText: "用户名",
hintText: "用户名或邮箱",
prefixIcon: Icon(Icons.person)),
),
TextField(
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock),
labelText: "密码",
hintText: "您的登录密码",
hintStyle: TextStyle(color: Colors.grey, fontSize: 13.0)),
obscureText: true,
)
],
));
}
}
运行效果如下图所示:
我们成功的自定义了下划线颜色和提问文字样式,细心的读者可能已经发现,通过这种方式自定义后,输入框在获取焦点时,labelText
不会高亮显示了,正如上图中的"用户名"本应该显示蓝色,但现在却显示为灰色,并且我们还是无法定义下划线宽度。另一种灵活的方式是直接隐藏掉TextField
本身的下划线,然后通过Container
去嵌套定义样式,如:
Container(
child: TextField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: "Email",
hintText: "电子邮件地址",
prefixIcon: Icon(Icons.email),
border: InputBorder.none //隐藏下划线
)
),
decoration: BoxDecoration(
// 下滑线浅灰色,宽度1像素
border: Border(bottom: BorderSide(color: Colors.grey[200], width: 1.0))
),
)
运行效果如下图:
通过这种组件组合的方式,也可以定义背景圆角等。一般来说,优先通过decoration
来自定义样式,如果decoration
实现不了,再用widget组合的方式。
八、示例
8.1 示例1:登录输入框
我们实现一个登录表单:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TextField(
autofocus: true,
decoration: InputDecoration(
labelText: "用户名",
hintText: "用户名或邮箱",
prefixIcon: Icon(Icons.person)),
),
TextField(
decoration: InputDecoration(
labelText: "密码",
hintText: "您的登录密码",
prefixIcon: Icon(Icons.lock)),
obscureText: true,
),
],
);
}
}
运行后,效果如下图所示:
8.2 示例2:完整的登录输入框
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
home: CustomTextFieldPage(),
));
}
class CustomTextFieldPage extends StatefulWidget {
const CustomTextFieldPage({super.key});
@override
State<CustomTextFieldPage> createState() => _CustomTextFieldPageState();
}
class _CustomTextFieldPageState extends State<CustomTextFieldPage> {
// 创建焦点节点对象
final FocusNode _nameFocusNode = FocusNode();
final FocusNode _passwordFocusNode = FocusNode();
// 记录焦点状态
bool _nameHasFocus = false;
bool _passwordHasFocus = false;
@override
void initState() {
super.initState();
// 监听焦点变化
_nameFocusNode.addListener(_handleNameFocusChange);
_passwordFocusNode.addListener(_handlePasswordFocusChange);
}
@override
void dispose() {
// 移除焦点监听
_nameFocusNode.removeListener(_handleNameFocusChange);
_passwordFocusNode.removeListener(_handlePasswordFocusChange);
_nameFocusNode.dispose();
_passwordFocusNode.dispose();
super.dispose();
}
void _handleNameFocusChange() {
// 处理姓名输入框焦点变化
setState(() {
_nameHasFocus = _nameFocusNode.hasFocus;
});
}
void _handlePasswordFocusChange() {
// 处理密码输入框焦点变化
setState(() {
_passwordHasFocus = _passwordFocusNode.hasFocus;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('自定义输入框'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'请输入您的姓名',
style: TextStyle(fontSize: 18.0),
),
const SizedBox(height: 16.0),
TextField(
focusNode: _nameFocusNode,
decoration: InputDecoration(
filled: true,
fillColor:
_nameHasFocus ? Colors.lightBlue[100] : Colors.grey[200],
border: const OutlineInputBorder(),
hintText: '请输入姓名',
hintStyle: const TextStyle(color: Colors.grey),
prefixIcon: const Icon(Icons.person),
),
),
const SizedBox(height: 16.0),
TextField(
focusNode: _passwordFocusNode,
obscureText: true,
decoration: InputDecoration(
filled: true,
fillColor: _passwordHasFocus
? Colors.lightBlue[100]
: Colors.grey[200],
border: const OutlineInputBorder(),
hintText: '请输入密码',
hintStyle: const TextStyle(color: Colors.grey),
prefixIcon: const Icon(Icons.lock),
),
),
],
),
),
),
);
}
}
运行后,效果如下图所示:
代码解析:
-
这段代码的功能是展示一个包含两个自定义样式输入框的页面,并在输入框获得焦点和失去焦点时改变其样式。
-
在这段代码中,我们使用了
FocusNode
来监听输入框的焦点状态。FocusNode
是一个用于管理焦点的类,在输入框获得或失去焦点时会触发相应的回调函数。 -
具体来说,代码中的
_nameFocusNode
和_passwordFocusNode
是两个FocusNode
实例,分别用于管理姓名输入框和密码输入框的焦点。 -
通过在
initState()
方法中为每个FocusNode
添加监听器(addListener),我们能够在焦点状态发生变化时得到通知。具体来说,我们为_nameFocusNode
添加了_handleNameFocusChange
回调函数,为_passwordFocusNode
添加了_handlePasswordFocusChange
回调函数。 -
当输入框获得焦点时,
_handleNameFocusChange
或_handlePasswordFocusChange
回调函数会被触发。在这些回调函数中,我们使用setState()
方法来更新对应的_nameHasFocus
和_passwordHasFocus
变量,以记录焦点的状态。 -
在
build()
方法中,我们根据_nameHasFocus
和_passwordHasFocus
变量的值来决定输入框的背景颜色。当输入框获得焦点时,背景颜色设置为浅蓝色(Colors.lightBlue[100]
),当输入框失去焦点时,背景颜色设置为灰色(Colors.grey[200]
)。 -
通过这种方式,我们能够根据输入框的焦点状态动态地改变其样式,提供更直观的用户反馈。当用户点击输入框时,输入框会获得焦点,背景颜色变为浅蓝色;当用户点击其他区域时,输入框失去焦点,背景颜色恢复为灰色。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库