使用 SelectionArea + Text.rich + TextSpan + WidgetSpan实现富文本。
前置知识点学习
SelectionArea
`SelectionArea` 是 Flutter 中的一个组件,用于管理文本的选择功能。它允许用户在应用中选择和复制文本,这是在支持文本选择的应用程序中常见的功能。`SelectionArea` 提供了一种简单而有效的方法来启用或禁用子树中的文本选择。
`SelectionArea` 的基本使用
`SelectionArea` 是一个小部件,通常用于包裹其他文本小部件,例如 `Text` 或 `RichText`,以允许用户选择和复制其中的文本。它的功能类似于网页上的文本选择。
示例代码
以下是一个简单的示例,展示如何使用 `SelectionArea` 来使文本可选择:
import 'package:flutter/material.dart';
class SelectionAreaExample extends StatelessWidget {
const SelectionAreaExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Selection Area Example")),
body: const Center(
child: SelectionArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'This text can be selected.',
style: TextStyle(fontSize: 18),
),
Text(
'You can select and copy this text.',
style: TextStyle(fontSize: 18),
),
],
),
),
),
);
}
}
解释
`SelectionArea`:
- `SelectionArea` 包裹了一个或多个文本小部件,使得这些文本可被用户选择和复制。
- 当用户长按或双击文本时,系统会显示选择控件,允许用户选择文本。
嵌套结构:
- 在 `SelectionArea` 内,可以嵌套任何小部件。在示例中,使用 `Column` 布局多个 `Text` 小部件。
- 所有被 `SelectionArea` 包裹的文本小部件都将支持选择功能。
复制功能:
- 一旦文本被选择,用户可以使用设备的系统功能(通常是长按弹出的菜单)来复制选定的文本。
使用场景
- 文档阅读器: 在阅读器应用中,使得用户可以选择和复制文本内容。
- 教育应用: 允许学生选择文本进行复制或分享。
- 消息应用: 在聊天界面中,使得用户可以选择和复制聊天记录。
注意事项
- 选择禁用: 可以在特定的文本区域使用 `SelectionContainer.disabled` 来禁用选择功能,这对于需要保护某些文本不被复制的场景很有用。
- 兼容性: `SelectionArea` 组件的行为依赖于平台的文本选择功能,不同平台可能有不同的文本选择体验。
通过 `SelectionArea`,开发者可以轻松地在应用中支持文字选择和复制功能,这对于增强用户体验和文本交互非常有用。
WidgetSpan
`WidgetSpan` 是 Flutter 中用于在富文本中嵌入小部件的一个非常有用的工具。它允许你在文本流中插入任意的 Flutter 小部件,提供了创建复杂布局的灵活性。`WidgetSpan` 是 `InlineSpan` 的一个子类,它可以与 `TextSpan` 结合使用在 `RichText` 中。
`WidgetSpan` 的基本概念
`WidgetSpan` 是 `InlineSpan` 的一个子类。它不像 `TextSpan` 仅用于显示文本,而是用于显示小部件。这使得你可以在文本中插入图标、图片、按钮等,增强文本的表现力和交互性。
使用 `WidgetSpan`
`WidgetSpan` 的一个常见用法是与 `RichText` 和 `TextSpan` 结合使用,以创建混合内容的布局。下面是一个简单的例子,展示如何在文本中插入一个图标:
import 'package:flutter/material.dart';
class WidgetSpanExample extends StatelessWidget {
const WidgetSpanExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("WidgetSpan Example")),
body: Center(
child: RichText(
text: const TextSpan(
style: TextStyle(fontSize: 18, color: Colors.blueGrey),
children: <InlineSpan>[
TextSpan(text: "Here is a star: "),
WidgetSpan(
child: Icon(
Icons.star,
color: Colors.blue,
size: 24,
)),
TextSpan(text: ' and some more text.')
]),
),
),
);
}
}
解释
`RichText` 和 `TextSpan`:
- `RichText` 用于显示和管理复杂的文本布局。
- `TextSpan` 定义文本的样式和内容。
`WidgetSpan`:
- `WidgetSpan` 在文本流中插入一个小部件。
- 在示例中,它插入了一个 `Icon` 小部件,让图标成为文本的一部分。
灵活性:
- 通过使用 `WidgetSpan`,你可以在文本中嵌入任何小部件。这使得文本布局非常灵活,可以包含动态内容。
使用场景
- 图文混排: 在文本中插入图片、图标或其他小部件。
- 动态内容: 嵌入需要动态更新的小部件,如计数器、进度条等。
- 装饰性元素: 在文本中插入装饰性元素,如分隔线、徽章等。
注意事项
- 布局影响: `WidgetSpan` 会影响文本的布局,因为它插入的是小部件而不是纯文本。需要考虑小部件的尺寸和对齐方式。
- 性能考虑: 嵌入复杂的小部件可能会影响性能,尤其是在需要频繁重绘的场景中。
通过 `WidgetSpan`,开发者可以在富文本中实现更复杂的布局和交互效果,支持更丰富的用户界面设计。
SizedBox
`SizedBox` 是 Flutter 中的一个常用布局小部件,主要用于在布局中创建具有特定尺寸的空白区域或调整其子小部件的尺寸。它是一个非常简单但功能强大的小部件,能够帮助开发者精确地控制 UI 元素的大小和布局。
基本用法
`SizedBox` 可以通过指定宽度和高度来定义其尺寸。当不包含子小部件时,它可以用作在布局中创建固定大小的空白区域;当包含子小部件时,它会调整子小部件的尺寸以适应指定的尺寸限制。
构造函数
SizedBox({
Key? key,
double? width,
double? height,
Widget? child,
})
属性
- `width`: 指定 `SizedBox` 的宽度。如果未指定,则宽度由子小部件决定或由父布局约束。
- `height`: 指定 `SizedBox` 的高度。如果未指定,则高度由子小部件决定或由父布局约束。
- `child`: 可选的子小部件。如果存在子小部件,`SizedBox` 会限制其尺寸;如果没有,`SizedBox` 只是一个固定大小的空白区域。
示例代码
下面是一些常见的 `SizedBox` 用法示例:
用作空白区域
SizedBox(
width: 100,
height: 100,
)
这段代码创建了一个 100x100 像素的空白区域。
调整子小部件尺寸
SizedBox(
width: 200,
height: 100,
child: Container(
color: Colors.blue,
),
)
这段代码创建一个 200x100 像素的蓝色矩形。`SizedBox` 将 `Container` 的尺寸调整为指定的宽度和高度。
仅指定一个维度
SizedBox(
height: 50,
child: Text("Hello, world!"),
)
这段代码将文本高度限制为 50 像素,宽度则由文本内容决定。
使用场景
- 间距控制: 使用 `SizedBox` 在小部件之间创建固定大小的间距。
- 尺寸约束: 限制子小部件的尺寸,使其符合特定的布局需求。
- 占位符: 用于在布局中为未来的小部件预留空间。
注意事项
- 无子小部件时的行为: 当 `SizedBox` 没有子小部件时,它可以用作固定尺寸的空白区域。
- 尺寸为零的行为: 如果 `width` 和 `height` 都设置为 `0`,`SizedBox` 不会占用任何空间。
`SizedBox` 是一个非常灵活的小部件,广泛用于布局调整和空间管理,帮助开发者精确控制 Flutter 应用的用户界面布局。
BoxFit
`BoxFit` 是 Flutter 中的一个枚举类,用于定义如何在布局中调整图像或其他内容的尺寸,以适应其显示容器的大小。它提供了多种模式来控制内容的拉伸或缩放方式,以确保在不同的容器中显示得当。
`BoxFit` 枚举值
`BoxFit` 包含多个枚举值,分别控制内容在容器内的适应方式:
`BoxFit.fill`:
- 拉伸内容以完全填充容器的宽度和高度。
- 不保留内容的宽高比,因此可能会导致图像失真。
`BoxFit.contain`:
- 缩放内容以适应容器,同时保持其宽高比。
- 内容会完全显示在容器内,可能会留有空白区域。
`BoxFit.cover`:
- 缩放内容以覆盖整个容器,同时保持其宽高比。
- 内容可能会被裁剪,以确保没有空白区域。
`BoxFit.fitWidth`:
- 缩放内容以适应容器的宽度,同时保持宽高比。
- 高度可能超出容器范围。
`BoxFit.fitHeight`:
- 缩放内容以适应容器的高度,同时保持宽高比。
- 宽度可能超出容器范围。
`BoxFit.none`:
- 不缩放内容,保持其原始尺寸。
- 内容可能会超出或小于容器范围。
`BoxFit.scaleDown`:
- 缩放内容以适应容器,但仅在内容尺寸大于容器时缩放。
- 实际效果类似于 `contain`,但不会放大内容。
示例使用
以下是如何在 `Container` 中使用 `BoxFit` 来控制图像的显示方式:
import 'package:flutter/material.dart';
class BoxFitExample extends StatelessWidget {
const BoxFitExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("BoxFit Example")),
body: Center(
child: Column(
children: [
Container(
width: 300,
height: 200,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("static/demo.png"),
fit: BoxFit.cover)),
),
const Text("BoxFit.cover Example"),
],
),
),
);
}
}
使用场景
- 背景图片: 使用 `BoxFit.cover` 或 `BoxFit.fill` 使背景图片填满整个区域。
- 图像显示: 使用 `BoxFit.contain` 或 `BoxFit.scaleDown` 确保完整显示图片而不裁剪。
- 特定布局需求: 根据不同的 UI 需求选择合适的 `BoxFit` 模式,以实现最佳视觉效果。
注意事项
- 选择合适的模式: 根据图像的用途和布局需求选择合适的 `BoxFit` 模式,以避免不必要的裁剪或失真。
- 性能考虑: 当处理大量或高分辨率图像时,要注意图像的加载和渲染性能,特别是在使用模式如 `BoxFit.fill` 时。
SelectionContainer
`SelectionContainer` 是 Flutter 提供的一个小部件,用于管理和控制其子树中内容的选择行为。它与 `SelectionArea` 配合使用,可以细化对某些内容是否可以被选中的控制。
`SelectionContainer` 的基本概念
`SelectionContainer` 允许开发者指定哪些内容可以被选中,以及在何种情况下可以被选中。它的主要功能是启用或禁用内容选择,提供更细粒度的控制。
常用属性
- `enabled`: 一个布尔值,表示是否启用选择功能。默认情况下,内容是可选中的。
- `disabled`: 这是一个常见的用法,通过 `SelectionContainer.disabled` 来显式禁用选择。
使用示例
下面是一个简单的示例,展示如何使用 `SelectionContainer` 来控制文本的选择行为:
import 'package:flutter/material.dart';
class SelectionContainerExample extends StatelessWidget {
const SelectionContainerExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Selection Container Example")),
body: const Center(
child: SelectionArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'This text can be selected.',
style: TextStyle(fontSize: 18),
),
SelectionContainer.disabled(
child: const Text(
'This text cannot be selected.',
style: TextStyle(fontSize: 18, color: Colors.grey),
))
],
),
),
),
);
}
}
解释
`SelectionArea`:
- `SelectionArea` 包裹在文本外层,使其默认情况下可选择。
`SelectionContainer`:
- `SelectionContainer` 用于细化选择控制。
- `SelectionContainer.disabled` 明确指定其子小部件不可被选中。
嵌套使用:
- 可以嵌套使用 `SelectionContainer` 和 `SelectionArea`,通过设置 `disabled` 来禁用特定部分的选择功能。
使用场景
- 启用/禁用文本选择: 在需要精确控制哪些文本段落可被选中的应用场景中非常有用。
- 保护敏感信息: 确保某些文本信息不被用户复制或选择。
- 定制用户交互: 根据应用的需求,定制用户如何与文本内容交互。
注意事项
- 默认行为: 在 `SelectionArea` 中的所有文本默认是可选中的,除非使用 `SelectionContainer.disabled` 来禁用。
- 嵌套效果: 在复杂布局中,注意嵌套的 `SelectionContainer` 和 `SelectionArea` 的影响,以确保选择行为符合预期。
通过 `SelectionContainer`,开发者可以在 Flutter 应用中提供更细致的文本选择控制,增强用户界面交互的灵活性和安全性。
ScaffoldMessenger
`ScaffoldMessenger` 是 Flutter 中用于显示 `SnackBar` 和处理 `SnackBar` 队列的一个重要工具。它提供了一种在整个应用范围内管理 `SnackBar` 的机制,无需依赖于特定的 `Scaffold` 实例。这在需要跨多个页面或上下文显示通知时特别有用。
`ScaffoldMessenger` 的基本概念
`ScaffoldMessenger` 是一个状态管理小部件,负责管理显示在 `Scaffold` 上的 `SnackBar`。它解决了以前版本中 `SnackBar` 的一些限制,使得显示 `SnackBar` 更加灵活和强大。
主要功能
- 管理 `SnackBar` 队列: `ScaffoldMessenger` 可以在一个队列中管理多个 `SnackBar`,并按顺序显示它们。
- 跨 `Scaffold` 显示: 允许在应用的任何位置显示 `SnackBar`,而不必绑定到特定的 `Scaffold`。
- 提供更好的状态管理: 通过 `ScaffoldMessenger`,可以在应用的不同部分之间更好地管理和共享 `SnackBar` 状态。
使用示例
以下是一个示例,展示如何使用 `ScaffoldMessenger` 来显示 `SnackBar`:
import 'package:flutter/material.dart';
class ScaffoldMessengerExample extends StatelessWidget {
const ScaffoldMessengerExample({super.key});
void _showSnackBar(BuildContext context) {
final snackBar = SnackBar(
content: const Text('This is a SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("ScaffoldMessenger Example")),
body: Center(
child: ElevatedButton(
onPressed: () => _showSnackBar(context),
child: const Text("Show SnackBar"),
),
),
);
}
}
解释
`ScaffoldMessenger.of(context)`:
- 用于获取与给定 `BuildContext` 关联的 `ScaffoldMessenger` 实例。
- 通过这个实例,可以调用 `showSnackBar` 来显示 `SnackBar`。
`SnackBar` 和 `SnackBarAction`:
- `SnackBar` 是一个临时显示的消息条,通常在底部显示。
- `SnackBarAction` 提供了一个可选的动作按钮供用户交互。
跨页面显示:
- 因为 `ScaffoldMessenger` 与应用的 `BuildContext` 相关联,所以可以在应用的任何地方调用,而不局限于当前页面的 `Scaffold`。
使用场景
- 全局通知: 在应用的任何位置显示通知或消息,而不依赖于特定页面的 `Scaffold`。
- 状态反馈: 在操作后向用户提供反馈,比如操作成功或失败的消息。
- 多页面应用: 在多页面应用中管理 `SnackBar`,确保持续的用户体验。
注意事项
- 队列管理: `ScaffoldMessenger` 自动管理 `SnackBar` 队列,通过调用 `showSnackBar` 可以不断添加新的 `SnackBar`。
IconButton
`IconButton` 是 Flutter 中的一个小部件,用于创建一个带有图标的可点击按钮。它是一个无状态的小部件,提供了一种简洁的方式来为用户界面添加交互元素。
`IconButton` 的基本属性
`IconButton` 的主要功能是展示一个图标,并在用户点击时触发一个回调函数。以下是 `IconButton` 的常用属性:
- `icon`: 这是一个 `Widget`,通常是一个 `Icon`,用于指定按钮中显示的图标。
- `onPressed`: 一个回调函数,当按钮被点击时调用。如果为 `null`,按钮会被禁用(不可点击),并显示禁用状态。
- `iconSize`: 用于设置图标的大小,默认为 24.0。
- `color`: 用于指定图标的颜色。
- `splashColor`: 点击按钮时的水波纹颜色。
- `highlightColor`: 按下按钮时的颜色。
- `padding`: 指定按钮的内边距。默认值是 `EdgeInsets.all(8.0)`。
- `tooltip`: 当用户长按或将鼠标悬停在按钮上时显示的文本提示。
使用示例
下面是一个简单的 `IconButton` 使用示例,展示如何在应用中创建一个带有图标的按钮:
import 'package:flutter/material.dart';
class IconButtonExample extends StatelessWidget {
const IconButtonExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("IconButton Example")),
body: Center(
child: IconButton(
icon: const Icon(Icons.volume_up),
onPressed: () {
// Define the action to take when the button is pressed
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Volume Up button pressed")),
);
},
color: Colors.blue, // Icon color
iconSize: 40.0, // Icon size
tooltip: 'Increase volume', // Tooltip text
),
),
);
}
}
解释
`icon` 属性:
- 设置为 `Icon(Icons.volume_up)`,这表示按钮上显示的图标。
`onPressed` 属性:
- 定义了一个简单的回调,使用 `ScaffoldMessenger` 显示一个 `SnackBar`,作为点击按钮时的反馈。
其他属性:
- `color` 设置图标颜色为蓝色。
- `iconSize` 将图标的尺寸设置为 40 像素。
- `tooltip` 提供了一个简短的说明,当用户长按或悬停时显示。
使用场景
- 工具栏按钮: 常用于应用程序的工具栏或导航栏中,提供快速操作。
- 可交互图标: 在页面中提供图标按钮,执行特定任务,如播放、暂停、音量控制等。
- 视觉提示: 通过图标直观地向用户传达按钮的功能。
注意事项
- 禁用状态: 如果 `onPressed` 设置为 `null`,按钮将显示为禁用状态,用户无法点击。
- 无文本标签: `IconButton` 仅用于显示图标,如果需要图标和文本一起使用,考虑使用 `TextButton` 或 `ElevatedButton` 等。
富文本代码学习
import 'package:flutter/material.dart';
class RichTextDemoPage2 extends StatefulWidget {
const RichTextDemoPage2({super.key});
@override
_RichTextDemoState2 createState() => _RichTextDemoState2();
}
class _RichTextDemoState2 extends State<RichTextDemoPage2> {
double size = 50;
@override
Widget build(BuildContext mainContext) {
return Scaffold(
appBar: AppBar(
title: const Text("RichTextDemoPage"),
actions: <Widget>[
IconButton(
onPressed: () {
setState(() {
size += 10;
});
},
icon: const Icon(Icons.add_circle_outline),
),
IconButton(
onPressed: () {
setState(() {
size -= 10;
});
},
icon: const Icon(Icons.remove_circle_outline),
)
],
),
body: SelectionArea(
child: Container(
margin: const EdgeInsets.all(10),
child: Builder(builder: (context) {
return Center(
child: Text.rich(TextSpan(
children: <InlineSpan>[
const TextSpan(text: 'Flutter is'),
const WidgetSpan(
child: SizedBox(
width: 120,
height: 50,
child: Card(
color: Colors.blue,
child: Center(child: Text('Hello World!'))),
)),
WidgetSpan(
child: SizedBox(
width: size > 0 ? size : 0,
height: size > 0 ? size : 0,
child: Image.asset(
"static/demo.png",
fit: BoxFit.cover,
),
)),
const TextSpan(text: 'the best!'),
const WidgetSpan(
child: SelectionContainer.disabled(
child: Text(' not copy'),
),
),
],
)),
);
}),
),
),
);
}
}