import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; /// ============================================================ /// Text 文本组件各种细节演示页 /// /// 一、两种使用方式: /// 1. 直接运行本页面:runApp(const MaterialApp(home: TextDemoPage())) /// 2. 复制某段核心代码到任意 Scaffold 的 body 中。 /// /// 二、Text 全家桶速记: /// 单样式文字 → Text /// 富文本拼接 → Text.rich + TextSpan /// 全局默认样式 → DefaultTextStyle(MaterialApp 自带,Text 会继承) /// /// 三、Flutter 3.x 注意: /// textScaleFactor 已废弃 → 用 textScaler / MediaQuery.textScalerOf(context) /// 颜色的 withOpacity 已废弃 → 用 withValues(alpha: x) /// ============================================================ class TextDemoPage extends StatelessWidget { const TextDemoPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Text 文本组件 Demo'), backgroundColor: Colors.blue), body: SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const _SectionTitle('1. 基础样式:fontSize / color / fontWeight / fontStyle'), Text( '默认样式', style: TextStyle(fontSize: 16.sp), ), SizedBox(height: 8.h), Text( '加粗 红色 斜体', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Color(0xFFE53935), fontStyle: FontStyle.italic, ), ), SizedBox(height: 24.h), const _SectionTitle('2. 对齐 textAlign(需有宽度约束)'), Container( width: 300.w, color: const Color(0xFFF5F7FA), child: const Column( // 关键:stretch 让每个子组件横向拉满,textAlign 才有空间生效 crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text('左对齐', textAlign: TextAlign.left), Text('居中对齐', textAlign: TextAlign.center), Text('右对齐', textAlign: TextAlign.right), ], ), ), SizedBox(height: 24.h), const _SectionTitle('3. 截断:maxLines + overflow(三件套必须配齐)'), // 要点:Text 必须有【有界宽度】溢出才生效,所以外面套了固定宽的 Container Container( width: 300.w, color: const Color(0xFFF5F7FA), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'ellipsis:这是一段非常非常非常长的文字,用来演示溢出省略号的效果,看看能不能截断显示', maxLines: 1, overflow: TextOverflow.ellipsis, // 末尾加省略号 ), SizedBox(height: 8.h), Text( 'clip:这是一段非常非常非常长的文字,用来演示溢出直接裁剪的效果,看看能不能截断显示', maxLines: 1, overflow: TextOverflow.clip, // 直接裁掉,不加省略号 ), SizedBox(height: 8.h), Text( // visible:文字真·溢出,直接画出灰色盒子边界(Container 默认不裁剪) 'softWrap:false:文字横向溢出灰色盒子边界,直接画到盒子外面去', softWrap: false, // 禁止自动换行 overflow: TextOverflow.visible, ), ], ), ), SizedBox(height: 24.h), const _SectionTitle('4. 行高 height(对比默认行高)'), Container( width: 300.w, color: const Color(0xFFF5F7FA), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '默认行高\n第二行内容', style: TextStyle(fontSize: 16.sp), ), SizedBox(height: 8.h), Text( 'height:1.2 紧凑\n第二行内容', style: TextStyle(fontSize: 16.sp, height: 1.2), ), SizedBox(height: 8.h), Text( 'height:2 松散\n第二行内容', style: TextStyle(fontSize: 16.sp, height: 2), ), ], ), ), SizedBox(height: 24.h), const _SectionTitle('5. 字间距 letterSpacing / 词间距 wordSpacing'), Text( '字间距 4:Flutter 框架真棒', style: TextStyle(fontSize: 16.sp, letterSpacing: 4), ), SizedBox(height: 8.h), Text( '词间距 8:Flutter Framework Great', style: TextStyle(fontSize: 16.sp, wordSpacing: 8), ), SizedBox(height: 24.h), const _SectionTitle('6. 富文本:Text.rich + TextSpan'), // 正确写法:recognizer 放 StatefulWidget 里创建并 dispose const _AgreementRichText(), SizedBox(height: 24.h), const _SectionTitle('7. 字体缩放 textScaler(Flutter 3 新写法)'), // 旧写法 textScaleFactor 已废弃;textScaler 按倍数放大 Text( '线性放大 1.5 倍', style: TextStyle(fontSize: 14.sp), textScaler: TextScaler.linear(1.5), ), SizedBox(height: 8.h), // 跟随系统设置:MaterialApp 已按系统字体缩放,也可显式读取 Builder( builder: (context) { final ts = MediaQuery.textScalerOf(context); return Text( '当前系统缩放:${ts.scale(14).toStringAsFixed(1)}px(原本14px)', style: TextStyle(fontSize: 14.sp), ); }, ), ], ), ), ); } } /// 区块标题 class _SectionTitle extends StatelessWidget { const _SectionTitle(this.title); final String title; @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only(bottom: 12.h), child: Text( title, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), ); } } /// 富文本协议段落 —— StatefulWidget 版本 /// 关键点:TapGestureRecognizer 必须在 dispose() 里释放,防止内存泄漏 class _AgreementRichText extends StatefulWidget { const _AgreementRichText(); @override State<_AgreementRichText> createState() => _AgreementRichTextState(); } 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() { // 关键:释放 recognizer,与 createState 一一对应 _agreementRecognizer.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Text.rich( TextSpan( children: [ TextSpan( text: '已阅读并同意', style: TextStyle(fontSize: 14.sp, color: Color(0xFF888888)), ), TextSpan( text: '《用户协议》', style: TextStyle( fontSize: 14.sp, color: Color(0xFF3B82F6), fontWeight: FontWeight.bold, ), recognizer: _agreementRecognizer, ), ], ), ); } }