一、为什么需要"文本高亮标记器"?
在 OpenHarmony 的多设备协同场景中,用户常需快速定位信息:
- 在长篇会议纪要中查找"决策";
- 在设备日志中定位"ERROR";
- 在跨端共享文档中标记重点词汇。
虽然系统级搜索功能存在,但应用内高亮能提供更沉浸、更即时的体验------无需跳转,一眼锁定目标。
然而,许多开发者误以为高亮需依赖富文本库或 HTML 渲染。实际上,仅用 Flutter 内置的 Text.rich 和 TextSpan,即可实现高效、安全、可定制的关键词高亮。
本文将构建一个极简页面:「文本高亮关键词标记器」。它只包含:
- 一个多行文本输入框(用于粘贴原文);
- 一个单行输入框(用于输入关键词);
- 一个"高亮"按钮;
- 一个只读结果显示区(自动高亮匹配词)。
所有逻辑基于字符串分割与重组,无正则、无插件、无性能隐患。
二、完整可运行代码
dart
// lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '文本高亮器',
debugShowCheckedModeBanner: false,
theme: ThemeData(useMaterial3: true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.green)),
home: const HighlightTextPage(),
);
}
}
class HighlightTextPage extends StatefulWidget {
const HighlightTextPage({super.key});
@override
State<HighlightTextPage> createState() => _HighlightTextPageState();
}
class _HighlightTextPageState extends State<HighlightTextPage> {
final _textController = TextEditingController();
final _keywordController = TextEditingController();
List<TextSpan> _highlightedSpans = [const TextSpan(text: '请输入文本和关键词,点击"高亮"')];
void _highlightText() {
final text = _textController.text;
final keyword = _keywordController.text.trim();
if (keyword.isEmpty) {
setState(() {
_highlightedSpans = [TextSpan(text: text.isEmpty ? '请输入文本' : text)];
});
return;
}
final parts = text.split(keyword);
final spans = <TextSpan>[];
for (int i = 0; i < parts.length; i++) {
spans.add(TextSpan(text: parts[i]));
if (i < parts.length - 1) {
spans.add(TextSpan(
text: keyword,
style: const TextStyle(backgroundColor: Colors.yellow, fontWeight: FontWeight.bold),
));
}
}
setState(() {
_highlightedSpans = spans;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('文本高亮标记器')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(controller: _textController, maxLines: 3, decoration: const InputDecoration(labelText: '输入原文')),
TextField(controller: _keywordController, decoration: const InputDecoration(labelText: '输入关键词')),
const SizedBox(height: 12),
ElevatedButton(onPressed: _highlightText, child: const Text('高亮')),
const SizedBox(height: 16),
Expanded(
child: SingleChildScrollView(
child: RichText(
text: TextSpan(style: DefaultTextStyle.of(context).style, children: _highlightedSpans),
),
),
),
],
),
),
);
}
}
✅ 此代码仅使用 Flutter 内置组件 (
TextField,RichText,TextSpan等),无需任何权限或外部依赖,在 OpenHarmony DevEco 模拟器中可直接运行。输入文本和关键词,点击"高亮",匹配词将以黄色背景加粗显示。
三、核心思想:用字符串分割实现高亮
高亮的本质是将原文按关键词切分为若干段,然后在每段之间插入"已样式化的关键词"。
例如:
原文:"OpenHarmony 是开源的"
关键词:"开源"
分割结果:["OpenHarmony 是", "的"]
重组后:[普通文本 "OpenHarmony 是"] + [高亮文本 "开源"] + [普通文本 "的"]
这种方法简单、高效、可预测,避免了正则表达式的复杂性和潜在安全风险(如 ReDoS)。
四、高亮逻辑实现:
我们首先看核心高亮算法:
dart
void _highlightText() {
final text = _textController.text;
final keyword = _keywordController.text.trim();
if (keyword.isEmpty) {
setState(() {
_highlightedSpans = [TextSpan(text: text.isEmpty ? '请输入文本' : text)];
});
return;
}
final parts = text.split(keyword);
final spans = <TextSpan>[];
for (int i = 0; i < parts.length; i++) {
spans.add(TextSpan(text: parts[i]));
if (i < parts.length - 1) {
spans.add(TextSpan(
text: keyword,
style: const TextStyle(backgroundColor: Colors.yellow, fontWeight: FontWeight.bold),
));
}
}
setState(() {
_highlightedSpans = spans;
});
}


这段代码实现了安全、高效的关键词高亮。
- 空值处理:若关键词为空,直接显示原文或提示,避免无效操作;
text.split(keyword):利用 Dart 内置字符串方法,按关键词分割;- 若关键词不存在,
parts长度为 1,循环中不会添加高亮项; - 若存在 N 次,
parts长度为 N+1,循环中会插入 N 次高亮关键词;
- 若关键词不存在,
TextSpan构建 :- 普通文本使用默认样式;
- 关键词使用黄色背景 + 加粗,视觉突出;
setState更新 :触发RichText重建,显示新结果。
💡 此方法区分大小写 。若需忽略大小写,可先将
text和keyword转为小写进行分割,但保留原文大小写用于显示------本文为简洁未实现,但扩展容易。
五、富文本渲染与布局:
再看 UI 渲染部分:
dart
Expanded(
child: SingleChildScrollView(
child: RichText(
text: TextSpan(style: DefaultTextStyle.of(context).style, children: _highlightedSpans),
),
),
)
这里展示了 RichText 的正确使用方式。
TextSpan样式继承 :- 外层
TextSpan设置style: DefaultTextStyle.of(context).style,确保普通文本继承当前主题字体、颜色; - 内层高亮
TextSpan仅覆盖backgroundColor和fontWeight,其余属性(如字号、颜色)自动继承;
- 外层
- 可滚动容器 :
Expanded占据剩余空间;SingleChildScrollView允许结果区域垂直滚动,避免内容溢出;
- 响应式布局 :
- 输入框固定高度,结果区自适应,适配不同屏幕尺寸。
📌 值得注意的是,
RichText不支持SelectableText的选择功能 。若需可选文本,可用SelectableText.rich替代(Flutter 3.0+ 支持),但 OpenHarmony 模拟器兼容性需验证。本文选用RichText以确保最大兼容性。
六、为何这个工具具有广泛适用性?
1. 开发者调试
- 快速验证日志中的关键字段;
- 模拟搜索结果高亮效果。
2. 终端用户场景
- 在共享笔记中标记重点;
- 在设备说明文档中查找参数。
3. 教学价值
- 演示
TextSpan和RichText的组合使用; - 展示字符串操作在 UI 中的直接应用;
- 体现"状态驱动视图"的 Flutter 核心思想。
更重要的是,它完全运行在应用沙盒内,不读取文件、不访问网络、不申请权限,符合 OpenHarmony 安全规范。
七、工程优化建议
1. 性能考虑
- 对超长文本(>10KB),
split和TextSpan列表可能影响性能; - 可限制输入长度:
TextField(maxLength: 1000)。
2. 功能扩展(保持简洁)
- 多关键词高亮 :用
List<String>存储关键词,嵌套分割; - 高亮颜色自定义:增加颜色选择器;
- 清除高亮 :重置
_highlightedSpans。
但本文坚持"单一功能极致简化",未加入额外复杂度。
八、结语:用基础能力,解决真实问题
本文的页面仅 69 行代码,却展示了如何用 Flutter 最基础的字符串和文本组件,实现一个实用、直观、安全的功能。它证明了:伟大的用户体验,往往源于对基础能力的深度掌握,而非对复杂框架的依赖。
在 OpenHarmony 的跨端开发中,我们应始终铭记:稳定、兼容、简洁,比炫技更重要。
让我们继续用这样的微创新,让技术真正服务于人。
🌐 欢迎加入开源鸿蒙跨平台社区 :
在这里,您将获得:
- OpenHarmony 富文本最佳实践;
- Flutter 字符串处理与 UI 映射技巧;
- 无依赖实用组件模板。
用基础,成就可靠。