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

4.9 KiB
Raw Blame History

【Demo】Text 文本组件全家桶

源文件:lib/pages/demo/text_demo.dart | 分类:基础布局 一句话:样式、对齐、截断、行高、富文本、可点击协议文本 —— 文本相关的细节一次看全。


一、这个 Demo 解决什么问题

Text 是 Flutter 里出现频率最高的组件,但细节坑不少:

  • textAlign 为什么有时不生效?
  • 超长文字怎么显示省略号?
  • "已阅读并同意《用户协议》"这种部分文字可点击怎么做?
  • 系统字体缩放怎么适配?

本页 7 个区块逐一演示。


二、怎么运行 / 演示内容

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 必须有宽度约束,对齐才有空间生效:

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();
  }
}

五、踩坑记录

  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)渲染 HTML 富文本(题目/解析), 底层就是 TextSpan 体系的自动生成;
  • 全局字体缩放:MaterialApp(builder: ...) 里统一包 MediaQuery 改造,实现"阅读字号调节"(见 rich_text_demo 的 Aa±);
  • 文本选择:SelectableText 让用户可复制(本项目的日志区就用它)。