在前面的章节中,我们已经构建了精美的静态页面(布局 + 样式)并处理了长列表(滚动)。现在的关键问题是:如何让用户与 App 进行交互? 比如登录输入密码、点击开关、弹出提示框等。
第三部分将聚焦于:
- 表单与输入 (Input & Forms):获取用户输入的核心组件,包括文本框、开关、单选框以及表单验证机制。
- 弹窗与反馈 (Dialogs & Alerts):如何优雅地打断用户或给予操作反馈。
第三部分:交互的核心——表单与反馈
第五章:表单与输入组件 (Input & Forms)
1. TextField (文本输入框)
说明:
TextField 是最常用的文本输入组件。它极其强大,属性繁多,可以定制键盘类型、外观装饰、密码隐藏等。
核心属性 (Properties):
| 属性名 | 类型 | 说明 |
|---|---|---|
controller | TextEditingController | 最重要。用于控制输入框的内容、获取输入值、监听变化。 |
decoration | InputDecoration | 用于控制输入框的外观(提示文字、边框、图标等)。 |
keyboardType | TextInputType | 键盘类型 (text, number, emailAddress, phone)。 |
obscureText | bool | 是否隐藏输入内容(用于密码),默认为 false。 |
onChanged | Function(String) | 文本发生变化时的回调。 |
maxLines | int | 最大行数。设为 1 为单行输入;设为 null 为多行自动伸缩。 |
InputDecoration 详解:
labelText: 悬浮标签(获取焦点时会上浮)。hintText: 提示占位符(类似 HTML placeholder)。prefixIcon/suffixIcon: 头部/尾部图标(如搜索图标、密码眼睛图标)。border/enabledBorder/focusedBorder: 定义不同状态下的边框样式。
代码示例 (基础输入):
import 'package:flutter/material.dart';
class TextFieldExample extends StatefulWidget {
_TextFieldExampleState createState() => _TextFieldExampleState();
}
class _TextFieldExampleState extends State<TextFieldExample> {
// 1. 创建控制器
final TextEditingController _userController = TextEditingController();
final TextEditingController _pwdController = TextEditingController();
void dispose() {
// 记得销毁控制器,释放内存
_userController.dispose();
_pwdController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Column(
children: [
// 用户名输入框
TextField(
controller: _userController,
decoration: InputDecoration(
labelText: "用户名",
hintText: "请输入手机号/邮箱",
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(), // 外边框风格
),
),
SizedBox(height: 20),
// 密码输入框
TextField(
controller: _pwdController,
obscureText: true, // 隐藏文字
decoration: InputDecoration(
labelText: "密码",
prefixIcon: Icon(Icons.lock),
suffixIcon: Icon(Icons.visibility), // 这里通常配合逻辑做点击切换显示/隐藏
),
),
ElevatedButton(
onPressed: () {
// 获取输入内容
print("User: ${_userController.text}, Pwd: ${_pwdController.text}");
},
child: Text("登录"),
)
],
);
}
}
2. Form & TextFormField (表单验证)
说明:
如果只是简单的输入,用 TextField 即可。但如果需要校验(如:必填、邮箱格式错误提示),则必须使用 Form 包裹 TextFormField。
机制:
- 使用
GlobalKey<FormState>绑定Form组件。 - 使用
TextFormField替代TextField,它多了一个validator属性。 - 通过 Key 调用
validate()方法触发所有子项的校验。
代码示例 (带验证的表单):
import 'package:flutter/material.dart';
class FormExample extends StatefulWidget {
_FormExampleState createState() => _FormExampleState();
}
class _FormExampleState extends State<FormExample> {
final _formKey = GlobalKey<FormState>(); // 1. 全局 Key
String _email = "";
Widget build(BuildContext context) {
return Form(
key: _formKey, // 绑定 Key
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: "邮箱"),
// 2. 校验逻辑
validator: (value) {
if (value == null || value.isEmpty) {
return '邮箱不能为空';
}
if (!value.contains('@')) {
return '请输入有效的邮箱格式';
}
return null; // 返回 null 代表校验通过
},
onSaved: (value) => _email = value!,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 3. 触发校验
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save(); // 调用 onSaved 保存数据
print("表单提交成功,邮箱: $_email");
}
},
child: Text("提交"),
)
],
),
);
}
}
3. 开关与选择 (Switch, Checkbox, Radio, Slider)
说明:
这些组件用于处理布尔值或选项选择。它们的共同点是无状态 (Stateless) 的外观,必须通过 onChanged 回调更新父组件的状态值(State),界面才会变化。
常用组件:
- Switch (开关): 类似 iOS 的设置开关。
- Checkbox (复选框): 方形打钩框。
- Radio (单选框): 圆形选择框,通常成组出现。
- Slider (滑块): 拖动选择数值。
代码示例:
import 'package:flutter/material.dart';
class SelectionExample extends StatefulWidget {
_SelectionExampleState createState() => _SelectionExampleState();
}
class _SelectionExampleState extends State<SelectionExample> {
bool _isSwitched = false;
bool _isChecked = false;
int _radioValue = 1;
double _sliderValue = 50.0;
Widget build(BuildContext context) {
return Column(
children: [
// 1. 开关
SwitchListTile(
title: Text("接收通知"),
value: _isSwitched,
onChanged: (val) {
setState(() => _isSwitched = val);
},
),
// 2. 复选框
CheckboxListTile(
title: Text("同意用户协议"),
value: _isChecked,
onChanged: (val) {
setState(() => _isChecked = val!);
},
),
// 3. 单选框 (男/女)
Row(
children: [
Text("性别: "),
Radio(
value: 1, // 该选项代表的值
groupValue: _radioValue, // 当前选中的值
onChanged: (int? val) {
setState(() => _radioValue = val!);
},
),
Text("男"),
Radio(
value: 2,
groupValue: _radioValue,
onChanged: (int? val) {
setState(() => _radioValue = val!);
},
),
Text("女"),
],
),
// 4. 滑块
Slider(
value: _sliderValue,
min: 0,
max: 100,
divisions: 10, // 分成10档
label: _sliderValue.round().toString(),
onChanged: (val) {
setState(() => _sliderValue = val);
},
)
],
);
}
}
第六章:弹窗与提示 (Dialogs & Alerts)
在 Flutter 中,弹窗通常通过“路由”机制推入一个新的层级。
1. AlertDialog (对话框)
说明:
标准的 Material Design 对话框,包含标题、内容和操作按钮。通常配合 showDialog 函数使用。
注意: showDialog 是一个异步方法 (Future),当对话框关闭时返回结果。
代码示例:
ElevatedButton(
child: Text("删除文件"),
onPressed: () async {
// 弹出对话框
bool? delete = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: Text("警告"),
content: Text("确定要删除这个文件吗?此操作无法撤销。"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false), // 关闭并返回 false
child: Text("取消"),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true), // 关闭并返回 true
child: Text("删除", style: TextStyle(color: Colors.red)),
),
],
);
},
);
if (delete == true) {
print("执行删除操作...");
}
},
);
2. SimpleDialog (简单选择框)
说明:
用于提供多个选项供用户选择。
代码示例:
SimpleDialog(
title: Text('选择语言'),
children: <Widget>[
SimpleDialogOption(
onPressed: () { Navigator.pop(context, '中文'); },
child: Padding(
padding: EdgeInsets.symmetric(vertical: 10),
child: Text('简体中文'),
),
),
SimpleDialogOption(
onPressed: () { Navigator.pop(context, 'English'); },
child: Padding(
padding: EdgeInsets.symmetric(vertical: 10),
child: Text('English'),
),
),
],
);
3. BottomSheet (底部弹窗)
说明:
从屏幕底部滑出的面板,常用于选择器或更多操作菜单。使用 showModalBottomSheet 触发。
代码示例:
ElevatedButton(
child: Text("更换头像"),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 150,
color: Colors.white,
child: Column(
children: [
ListTile(
leading: Icon(Icons.camera_alt),
title: Text("拍照"),
onTap: () => Navigator.pop(context),
),
ListTile(
leading: Icon(Icons.photo),
title: Text("从相册选择"),
onTap: () => Navigator.pop(context),
),
],
),
);
},
);
},
);
4. SnackBar (底部轻提示)
说明:
在屏幕底部短暂弹出的黑色提示条,通常用于提示“加载成功”、“已删除”等状态。不会打断用户操作。
重要: Flutter 2.0 之后,推荐使用 ScaffoldMessenger 来管理 SnackBar。
代码示例:
ElevatedButton(
child: Text("撤销操作"),
onPressed: () {
final snackBar = SnackBar(
content: Text('邮件已删除'),
duration: Duration(seconds: 3), // 持续时间
action: SnackBarAction(
label: '撤销',
onPressed: () {
// 这里执行撤销逻辑
print("用户点击了撤销");
},
),
);
// 显示 SnackBar
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
);
第三部分总结:
在本部分中,你学会了:
- TextField 和 Form:如何让用户输入数据并进行格式校验。
- StatefulWidget 交互:通过 Switch、Checkbox 等组件理解了 Flutter “状态驱动 UI” 的理念。
- Dialog 和 SnackBar:掌握了 App 中最常用的反馈机制。
现在的你已经具备了开发一个功能性 App(如待办事项清单、登录注册页)的核心能力。
后续预告:
App 通常不仅仅只有一个页面。
第四部分 将进入 多页面管理与导航 (Navigation & Routing)。
我们将学习:
- 如何从 A 页面跳转到 B 页面(
Navigator.push)。 - 如何传递参数(例如点击商品列表进入详情页)。
- 底部导航栏 (
BottomNavigationBar) 和 侧边栏 (Drawer) 的完整实现。

780

被折叠的 条评论
为什么被折叠?



