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

149 lines
4.9 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】Text 文本组件全家桶
> 源文件:`lib/pages/demo/text_demo.dart` | 分类:基础布局
> 一句话:样式、对齐、截断、行高、富文本、可点击协议文本 —— 文本相关的细节一次看全。
---
## 一、这个 Demo 解决什么问题
Text 是 Flutter 里出现频率最高的组件,但细节坑不少:
- `textAlign` 为什么有时不生效?
- 超长文字怎么显示省略号?
- "已阅读并同意《用户协议》"这种**部分文字可点击**怎么做?
- 系统字体缩放怎么适配?
本页 7 个区块逐一演示。
---
## 二、怎么运行 / 演示内容
```dart
runApp(const MaterialApp(home: TextDemoPage()));
```
1. 基础样式(fontSize / color / fontWeight / fontStyle);
2. 对齐 textAlign(左/中/右);
3. 截断三件套(ellipsis / clip / softWrap:false);
4. 行高 height 对比(默认 / 1.2 / 2);
5. 字间距 letterSpacing / 词间距 wordSpacing;
6. 富文本 Text.rich + TextSpan + 可点击协议;
7. 字体缩放 textScaler。
---
## 三、核心知识点(新手向)
### 1. Text 全家桶速记
| 场景 | 组件 |
|---|---|
| 单样式文字 | `Text` |
| 富文本拼接(不同颜色/可点击段落) | `Text.rich` + `TextSpan` |
| 全局默认样式 | `DefaultTextStyle`(MaterialApp 自带,Text 会继承) |
### 2. textAlign 生效的前提
**Text 必须有宽度约束**,对齐才有空间生效:
```dart
Container(
width: 300.w,
child: Column(
// 关键:stretch 让每个子组件横向拉满,textAlign 才有空间生效
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('左对齐', textAlign: TextAlign.left),
Text('居中对齐', textAlign: TextAlign.center),
Text('右对齐', textAlign: TextAlign.right),
],
),
)
```
> 新手最常见的疑惑:在 Column 里写 `textAlign: center` 不居中 —— 因为 Text 宽度只包住文字。
> 要么 `crossAxisAlignment: center`(让 Text 居中),要么 `stretch` + `textAlign`(让文字在拉满的宽度里居中)。
### 3. 截断三件套(必须配齐)
```dart
Text(
'很长很长...',
maxLines: 1,
overflow: TextOverflow.ellipsis, // 末尾加省略号
)
```
- `ellipsis`:省略号;`clip`:直接裁掉;`softWrap: false` + `visible`:真·溢出画到盒子外;
- 前提同样是**有界宽度**(外面套固定宽 Container)。
### 4. 行高与间距
```dart
TextStyle(height: 1.2) // 行高倍数(1.0 = 紧凑,2.0 = 松散)
TextStyle(letterSpacing: 4) // 字间距(中文按字)
TextStyle(wordSpacing: 8) // 词间距(按空格分词,中文基本无效)
```
### 5. 富文本 + 可点击(协议文本的官方姿势)
```dart
Text.rich(
TextSpan(
children: [
TextSpan(text: '已阅读并同意', style: TextStyle(color: Color(0xFF888888))),
TextSpan(
text: '《用户协议》',
style: TextStyle(color: Color(0xFF3B82F6), fontWeight: FontWeight.bold),
recognizer: _agreementRecognizer, // 点击手势(必须 dispose!)
),
],
),
)
```
`TapGestureRecognizer` 必须放在 **StatefulWidget 里创建,并在 dispose() 里释放**(与 createState 一一对应,防内存泄漏)。
### 6. 字体缩放(Flutter 3.x)
```dart
Text('线性放大 1.5 倍', textScaler: TextScaler.linear(1.5));
// 读取系统缩放:MediaQuery.textScalerOf(context)
```
---
## 四、关键代码剖析
```dart
class _AgreementRichTextState extends State<_AgreementRichText> {
// late final:整个 State 生命周期只创建一次,复用同一个 recognizer
late final TapGestureRecognizer _agreementRecognizer = TapGestureRecognizer()
..onTap = _handleAgreementTap;
void _handleAgreementTap() {
// 点击回调可能晚于 dispose(点击瞬间页面被切走),用 mounted 兜底
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('点了《用户协议》')),
);
}
@override
void dispose() {
_agreementRecognizer.dispose(); // 关键:释放 recognizer
super.dispose();
}
}
```
---
## 五、踩坑记录
1. **`textScaleFactor` 已废弃**(Flutter 3.x)→ 用 `textScaler` / `MediaQuery.textScalerOf(context)`;
2. **`withOpacity` 已废弃** → 用 `withValues(alpha: x)`;
3. textAlign 不生效 = 没有宽度约束(用 stretch 或固定宽);
4. 截断不生效 = 没有 `maxLines`(三件套要配齐);
5. `TapGestureRecognizer` 不 dispose 会内存泄漏(页面反复进出后崩溃/卡顿)。
---
## 六、扩展思考
- 富文本进阶:本项目的 `RichTextContent`([rich_text_demo.md](rich_text_demo.md))渲染 HTML 富文本(题目/解析),
底层就是 TextSpan 体系的自动生成;
- 全局字体缩放:`MaterialApp(builder: ...)` 里统一包 `MediaQuery` 改造,实现"阅读字号调节"(见 rich_text_demo 的 Aa±);
- 文本选择:`SelectableText` 让用户可复制(本项目的日志区就用它)。