4.9 KiB
4.9 KiB
【Demo】Text 文本组件全家桶
源文件:
lib/pages/demo/text_demo.dart| 分类:基础布局 一句话:样式、对齐、截断、行高、富文本、可点击协议文本 —— 文本相关的细节一次看全。
一、这个 Demo 解决什么问题
Text 是 Flutter 里出现频率最高的组件,但细节坑不少:
textAlign为什么有时不生效?- 超长文字怎么显示省略号?
- "已阅读并同意《用户协议》"这种部分文字可点击怎么做?
- 系统字体缩放怎么适配?
本页 7 个区块逐一演示。
二、怎么运行 / 演示内容
runApp(const MaterialApp(home: TextDemoPage()));
- 基础样式(fontSize / color / fontWeight / fontStyle);
- 对齐 textAlign(左/中/右);
- 截断三件套(ellipsis / clip / softWrap:false);
- 行高 height 对比(默认 / 1.2 / 2);
- 字间距 letterSpacing / 词间距 wordSpacing;
- 富文本 Text.rich + TextSpan + 可点击协议;
- 字体缩放 textScaler。
三、核心知识点(新手向)
1. Text 全家桶速记
| 场景 | 组件 |
|---|---|
| 单样式文字 | Text |
| 富文本拼接(不同颜色/可点击段落) | Text.rich + TextSpan |
| 全局默认样式 | DefaultTextStyle(MaterialApp 自带,Text 会继承) |
2. textAlign 生效的前提
Text 必须有宽度约束,对齐才有空间生效:
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. 截断三件套(必须配齐)
Text(
'很长很长...',
maxLines: 1,
overflow: TextOverflow.ellipsis, // 末尾加省略号
)
ellipsis:省略号;clip:直接裁掉;softWrap: false+visible:真·溢出画到盒子外;- 前提同样是有界宽度(外面套固定宽 Container)。
4. 行高与间距
TextStyle(height: 1.2) // 行高倍数(1.0 = 紧凑,2.0 = 松散)
TextStyle(letterSpacing: 4) // 字间距(中文按字)
TextStyle(wordSpacing: 8) // 词间距(按空格分词,中文基本无效)
5. 富文本 + 可点击(协议文本的官方姿势)
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)
Text('线性放大 1.5 倍', textScaler: TextScaler.linear(1.5));
// 读取系统缩放:MediaQuery.textScalerOf(context)
四、关键代码剖析
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();
}
}
五、踩坑记录
textScaleFactor已废弃(Flutter 3.x)→ 用textScaler/MediaQuery.textScalerOf(context);withOpacity已废弃 → 用withValues(alpha: x);- textAlign 不生效 = 没有宽度约束(用 stretch 或固定宽);
- 截断不生效 = 没有
maxLines(三件套要配齐); TapGestureRecognizer不 dispose 会内存泄漏(页面反复进出后崩溃/卡顿)。
六、扩展思考
- 富文本进阶:本项目的
RichTextContent(rich_text_demo.md)渲染 HTML 富文本(题目/解析), 底层就是 TextSpan 体系的自动生成; - 全局字体缩放:
MaterialApp(builder: ...)里统一包MediaQuery改造,实现"阅读字号调节"(见 rich_text_demo 的 Aa±); - 文本选择:
SelectableText让用户可复制(本项目的日志区就用它)。