Files
App.K12.Student.Miniapp_Flu…/docs/demos/input_demo.md
T
2026-09-04 15:22:20 +08:00

110 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 【Demo】TextField 输入框(登录表单)
> 源文件:`lib/pages/demo/input_demo.dart` | 分类:基础布局
> 一句话:输入框 + 密码"小眼睛"切换 —— 表单最基础的零件。
---
## 一、这个 Demo 解决什么问题
做一个最简单的登录表单:手机号输入框 + 密码输入框(可切换明文/密文)。
核心是搞懂 **TextEditingController** 的用法——Flutter 里输入框不叫"双向绑定",
而是"控制器持有值,代码里随时读写"。
---
## 二、怎么运行 / 演示内容
```dart
runApp(const MaterialApp(home: InputDemoPage()));
```
- 手机号框:弹数字键盘(`keyboardType: TextInputType.phone`);
- 密码框:右侧眼睛图标切换密文/明文;
- 点"登录":控制台打印两个输入框的内容。
---
## 三、核心知识点(新手向)
### 1. 对照 Vue 的 v-model
| Vue | Flutter |
|---|---|
| `v-model="phone"` | `TextEditingController` + `controller.text` |
| 框架自动同步 | 手动 `controller.text` 读写 |
Flutter 没有隐式的双向绑定:`controller.text` 改了就同步到输入框,
输入框内容变了 `controller.text` 也会读到 —— 但**没有"响应式"**:
想根据输入内容实时刷新 UI(如"输入满 11 位才能点登录"),要自己 `addListener` 或 `setState`。
### 2. TextEditingController 生命周期(红线)
```dart
final TextEditingController _userCtrl = TextEditingController();
@override
void dispose() {
_userCtrl.dispose(); // 必须在 dispose 释放,防内存泄漏
super.dispose();
}
```
> 凡是用 `Controller` 命名的东西(Text/Scroll/Animation/Tab),基本都要 dispose。
### 3. 密码眼睛切换
```dart
bool _showPwd = false;
TextField(
controller: _pwdCtrl,
obscureText: !_showPwd, // 密文/明文由状态决定
decoration: InputDecoration(
labelText: "密码",
prefixIcon: const Icon(Icons.lock),
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(_showPwd ? Icons.visibility : Icons.visibility_off),
onPressed: () => setState(() => _showPwd = !_showPwd),
),
),
)
```
### 4. InputDecoration 常用参数
- `labelText`:浮动标签(聚焦时缩小浮到边框上);
- `hintText`:灰色占位提示;
- `prefixIcon`:左侧图标;`suffixIcon`:右侧(按钮/图标);
- `border: OutlineInputBorder()`:带边框的圆角样式(默认是下划线)。
---
## 四、关键代码剖析
```dart
// 读取输入内容(点登录时)
String user = _userCtrl.text;
String pwd = _pwdCtrl.text;
if (kDebugMode) {
print("账号:$user,密码:$pwd"); // kDebugMode:只在 debug 包打印
}
```
要点:
- `kDebugMode`(来自 `package:flutter/foundation.dart`)保证打印不会出现在 release 包;
- 真实项目里读取后要走校验(见 [form_demo.md](form_demo.md))和登录接口(见 [dio_demo.md](dio_demo.md))。
---
## 五、踩坑记录
1. **Scaffold 没有 `padding` 参数!** 外间距要写在 body 的 `Padding` 上(新手必踩)。
2. `controller` 忘了 dispose → 内存泄漏(Flutter 分析器会提示)。
3. `suffixIcon` 用 `IconButton` 而不要用普通 `Icon` 包 `GestureDetector` —— IconButton 自带点击区域和涟漪反馈。
4. 想"输入即校验"别自己写监听,直接上 Form(见 form_demo)。
---
## 六、扩展思考
- 进阶校验:`TextFormField` + `validator`([form_demo.md](form_demo.md))—— 本项目表单校验全家桶;
- 真实登录页还要:防重复提交、加载动画、错误提示、密码安全存储(本项目用 flutter_secure_storage,见架构文档存储层);
- 输入法控制:`textInputAction: TextInputAction.next` 让回车键变成"下一项",最后一项变"完成"。