Files
2026-09-04 15:22:20 +08:00

239 lines
8.5 KiB
Dart
Raw Permalink 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.
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,
),
],
),
);
}
}