Flutter三方库适配OpenHarmony【palindrome_checker】回文检测器项目完整实战
Flutter三方库适配OpenHarmony【palindrome_checker】回文检测器项目完整实战
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
palindrome_checker 是一个基于 Flutter 的回文检测器项目,核心代码位于 lib/main.dart。用户输入单词、短语或句子后,应用会实时清洗文本:先转小写,再移除非英文小写字母和数字字符,然后反转清洗结果并与原清洗文本比较,最终展示是否为回文。页面还提供示例 ActionChip、结果卡片、Analysis 分析区和回文概念说明卡片。
这个项目适合学习 Flutter 文本工具在 OpenHarmony 上的适配过程。它覆盖了 TextEditingController 生命周期、onChanged 实时检测、RegExp 文本清洗、字符串反转、条件渲染、ActionChip 快捷示例、分析卡片复用 和 Material 3 工具型布局。

图片说明:本文围绕 Flutter 文本输入、字符串处理和 OpenHarmony 承载工程展开,所有关键代码均来自 palindrome_checker 的真实源码。
回文检测真正关键的是“清洗规则”。当前源码只保留
a-z和0-9,因此它适合英文和数字回文,不适合直接处理中文、重音字符或复杂 Unicode 文本。
一、项目背景与目标
1.1 项目定位
palindrome_checker 是一个轻量文本检测工具。用户可以直接输入文本,也可以点击示例 Chip 快速填入回文示例。应用会实时展示检测结果,并给出清洗后的文本、反转后的文本、有效字符数和匹配状态。
当前项目真实支持的功能包括:
- 输入单词、短语或句子。
- 输入变化时实时检测。
- 点击示例 Chip 自动填入文本。
- 将输入文本转为小写。
- 移除非
a-z0-9字符。 - 对清洗后的文本执行反转。
- 判断清洗文本和反转文本是否一致。
- 空文本不显示结果卡片。
- 回文结果展示绿色渐变卡片。
- 非回文结果展示红色渐变卡片。
- 分析区展示 Cleaned Text。
- 分析区展示 Reversed Text。
- 分析区展示 Character Count。
- 分析区展示 Match Status。
- 底部展示回文概念说明。
1.2 技术目标
本文围绕真实源码拆解以下内容:
- Flutter 应用入口和青色 Material 3 主题。
_controller如何管理输入框文本。_isPalindrome为什么使用bool?。_processedText和_reversedText如何保存分析结果。_examplePalindromes如何提供示例数据。_checkPalindrome如何清洗、反转和比较。_setExample如何填入示例并立即检测。- 结果卡片如何根据检测结果切换颜色和图标。
_buildAnalysisRow如何复用分析行。- OpenHarmony 侧如何验证输入、Chip、实时检测和滚动布局。
1.3 核心实现速览
| 能力 | 当前实现 | 适配关注点 |
|---|---|---|
| 应用入口 | runApp(const PalindromeCheckerApp()) |
确认首屏加载 |
| 主题 | ColorScheme.fromSeed(seedColor: Colors.cyan) |
确认青色 Material 3 样式 |
| 输入控制 | TextEditingController |
确认输入同步 |
| 实时检测 | onChanged: _checkPalindrome |
确认输入后立即刷新 |
| 文本清洗 | RegExp(r'[^a-z0-9]') |
确认仅保留英文数字 |
| 文本反转 | split('').reversed.join() |
确认反转结果 |
| 示例填入 | ActionChip |
确认点击填入并检测 |
| 结果展示 | Card + LinearGradient |
确认绿色和红色状态 |
| 分析区域 | _buildAnalysisRow |
确认分析行展示 |
| 说明卡片 | What is a Palindrome? |
确认概念说明 |
二、环境准备与工程结构
2.1 工程结构
项目保持 Flutter 标准结构,同时包含 OpenHarmony 平台工程。
| 文件或目录 | 作用 |
|---|---|
lib/main.dart |
应用入口、输入控制、回文检测和 UI |
pubspec.yaml |
SDK 约束、Flutter 依赖和 Material 图标配置 |
analysis_options.yaml |
Flutter lint 规则 |
test/ |
Flutter 测试目录 |
ohos/ |
OpenHarmony 平台承载工程 |
README.md |
项目说明文件 |
当前项目不依赖额外文本处理库,字符串清洗和反转全部使用 Dart 基础能力完成。
2.2 依赖配置
项目使用 Dart SDK ^3.9.2,依赖 Flutter SDK。
environment:
sdk: ^3.9.2
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
flutter:
uses-material-design: true
2.3 常用命令
flutter pub get
flutter analyze
flutter test
flutter run
| 命令 | 用途 |
|---|---|
flutter pub get |
获取依赖 |
flutter analyze |
执行静态分析 |
flutter test |
执行测试 |
flutter run |
在目标设备运行 |
OpenHarmony 调试时,还需要结合本地 Flutter OpenHarmony 工具链完成构建、安装和运行。
三、应用入口与主题配置
3.1 import 依赖
项目只引入 Flutter Material。
import 'package:flutter/material.dart';
material.dart 提供 MaterialApp、Scaffold、AppBar、TextField、ActionChip、Card、Icon 等组件。
3.2 main 函数
入口函数启动根组件。
void main() {
runApp(const PalindromeCheckerApp());
}
3.3 PalindromeCheckerApp
根组件创建 MaterialApp。
class PalindromeCheckerApp extends StatelessWidget {
const PalindromeCheckerApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Palindrome Checker',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.cyan),
useMaterial3: true,
),
home: const PalindromeCheckerHomePage(title: 'Palindrome Checker'),
);
}
}
这段代码包含三个关键点:
- 应用标题为
Palindrome Checker。 - 使用
Colors.cyan作为主题种子色。 - 首页为
PalindromeCheckerHomePage。
四、页面状态设计
4.1 StatefulWidget
输入和检测结果会实时变化,因此页面使用 StatefulWidget。
class PalindromeCheckerHomePage extends StatefulWidget {
const PalindromeCheckerHomePage({super.key, required this.title});
final String title;
State<PalindromeCheckerHomePage> createState() => _PalindromeCheckerHomePageState();
}
4.2 状态字段
状态类包含输入控制器和检测结果。
final TextEditingController _controller = TextEditingController();
bool? _isPalindrome;
String _processedText = '';
String _reversedText = '';
| 字段 | 类型 | 初始值 | 作用 |
|---|---|---|---|
_controller |
TextEditingController |
新实例 | 管理输入框 |
_isPalindrome |
bool? |
null |
保存是否为回文 |
_processedText |
String |
空字符串 | 清洗后的文本 |
_reversedText |
String |
空字符串 | 反转后的文本 |
4.3 bool? 的意义
bool? 可以表达三种状态:
| 值 | 含义 |
|---|---|
null |
尚未检测 |
true |
是回文 |
false |
不是回文 |
当前 UI 主要通过 _processedText.isNotEmpty 控制结果区显示,因此 null 状态不会直接展示。
4.4 生命周期释放
void dispose() {
_controller.dispose();
super.dispose();
}
TextEditingController 在页面销毁时释放,生命周期处理完整。
五、示例回文数据
5.1 示例列表
项目内置了 10 个示例。
final List<String> _examplePalindromes = [
'radar',
'level',
'civic',
'rotor',
'madam',
'refer',
'A man, a plan, a canal: Panama',
'Was it a car or a cat I saw?',
'No lemon, no melon',
'Step on no pets',
];
5.2 示例表
| 示例 | 类型 |
|---|---|
| radar | 单词 |
| level | 单词 |
| civic | 单词 |
| rotor | 单词 |
| madam | 单词 |
| refer | 单词 |
| A man, a plan, a canal: Panama | 短语 |
| Was it a car or a cat I saw? | 短语 |
| No lemon, no melon | 短语 |
| Step on no pets | 短语 |
5.3 当前展示数量
页面只展示前 5 个示例。
_examplePalindromes.take(5).map((example) {
return ActionChip(...);
}).toList()
因此用户能看到 radar、level、civic、rotor、madam。
六、输入框与示例 Chip
6.1 输入框
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Enter text to check',
hintText: 'Type a word or phrase...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
),
onChanged: _checkPalindrome,
)
输入变化时直接调用 _checkPalindrome。
6.2 示例标题
const Text(
'Try these examples:',
style: TextStyle(fontWeight: FontWeight.bold),
)
这行文案引导用户快速尝试示例。
6.3 ActionChip
示例使用 ActionChip 展示。
ActionChip(
label: Text(example.length > 15 ? '${example.substring(0, 15)}...' : example),
onPressed: () => _setExample(example),
)
超过 15 个字符的示例会截断显示,但当前页面只展示前 5 个短单词,不会触发截断。
6.4 _setExample
点击示例后填入输入框并立即检测。
void _setExample(String example) {
_controller.text = example;
_checkPalindrome(example);
}
这样用户不需要手动触发检测。
七、回文检测核心逻辑
7.1 _checkPalindrome
检测方法如下:
void _checkPalindrome(String text) {
final cleaned = text.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '');
final reversed = cleaned.split('').reversed.join();
setState(() {
_processedText = cleaned;
_reversedText = reversed;
_isPalindrome = cleaned.isNotEmpty && cleaned == reversed;
});
}
7.2 清洗规则
text.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '')
该规则做了两件事:
- 把文本转为小写。
- 移除所有不是英文小写字母或数字的字符。
7.3 反转规则
final reversed = cleaned.split('').reversed.join();
先拆成字符列表,再反转,再拼接。
7.4 判断规则
_isPalindrome = cleaned.isNotEmpty && cleaned == reversed;
空文本不是回文;非空且清洗文本等于反转文本时才是回文。
7.5 示例推导
以 A man, a plan, a canal: Panama 为例:
原文: A man, a plan, a canal: Panama
小写: a man, a plan, a canal: panama
清洗: amanaplanacanalpanama
反转: amanaplanacanalpanama
结果: 回文
八、结果卡片
8.1 显示条件
结果区只在 _processedText 不为空时显示。
if (_processedText.isNotEmpty) ...[
Card(...),
Card(...),
]
空输入时,页面只显示输入框、示例和说明卡片。
8.2 成功状态
回文时显示绿色渐变和 check 图标。
colors: _isPalindrome == true
? [Colors.green.shade100, Colors.green.shade50]
: [Colors.red.shade100, Colors.red.shade50]
Icon(
_isPalindrome == true ? Icons.check_circle : Icons.cancel,
size: 64,
color: _isPalindrome == true ? Colors.green : Colors.red,
)
8.3 结果文案
Text(
_isPalindrome == true ? 'PALINDROME!' : 'Not a Palindrome',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: _isPalindrome == true ? Colors.green : Colors.red,
),
)
回文与非回文的视觉反馈非常明确。
九、Analysis 分析卡片
9.1 分析卡片结构
结果卡片下面是 Analysis 分析区。
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Analysis'),
_buildAnalysisRow('Cleaned Text', _processedText),
const Divider(),
_buildAnalysisRow('Reversed Text', _reversedText),
const Divider(),
_buildAnalysisRow('Character Count', '${_processedText.length}'),
const Divider(),
_buildAnalysisRow(
'Match Status',
_isPalindrome == true ? 'Matches exactly' : 'Does not match',
),
],
),
),
)
9.2 分析项
| 分析项 | 含义 |
|---|---|
| Cleaned Text | 清洗后的文本 |
| Reversed Text | 反转后的文本 |
| Character Count | 清洗后有效字符数 |
| Match Status | 是否完全匹配 |
9.3 _buildAnalysisRow
分析行被封装为方法。
Widget _buildAnalysisRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(color: Colors.grey)),
Text(
value,
style: const TextStyle(
fontFamily: 'monospace',
fontWeight: FontWeight.bold,
),
),
],
),
);
}
使用等宽字体能让清洗文本和反转文本更容易比对。
十、说明卡片
10.1 What is a Palindrome
底部卡片解释回文概念。
Card(
color: Colors.cyan.shade50,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'What is a Palindrome?',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
const SizedBox(height: 8),
const Text(
'A palindrome is a word, phrase, number, or other sequence of characters '
'that reads the same forward and backward (ignoring spaces, punctuation, '
'and capitalization).',
),
],
),
),
)
10.2 示例说明
说明卡片使用第四个示例。
Text(
'Example: "${_examplePalindromes[3]}" reads the same forwards and backwards.',
style: TextStyle(color: Colors.cyan.shade700),
)
_examplePalindromes[3] 是 rotor。
10.3 规则一致性
说明文案提到忽略空格、标点和大小写,和 _checkPalindrome 的清洗逻辑一致。只是当前源码的正则更具体:保留的是英文小写字母和数字。
十一、页面布局
11.1 Scaffold 结构
页面使用 Scaffold。
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [],
),
),
);
11.2 SingleChildScrollView
内容区使用滚动容器。
SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [],
),
)
结果卡片、分析卡片和说明卡片全部显示时,小屏设备可以滚动访问。
11.3 页面顺序
| 顺序 | 区域 | 作用 |
|---|---|---|
| 1 | 输入框 | 输入待检测文本 |
| 2 | 示例 Chip | 快速填入示例 |
| 3 | 结果卡片 | 展示回文判断 |
| 4 | Analysis 卡片 | 展示清洗和反转细节 |
| 5 | 说明卡片 | 解释回文概念 |
十二、OpenHarmony 适配要点
12.1 基础组件验证
当前项目使用的 Flutter 组件包括:
| 组件 | 作用 | OpenHarmony 关注点 |
|---|---|---|
MaterialApp |
应用根组件 | 首屏加载 |
Scaffold |
页面骨架 | AppBar 与 Body |
TextField |
输入文本 | 软键盘、输入同步 |
ActionChip |
示例填入 | 点击响应 |
SingleChildScrollView |
页面滚动 | 小屏访问 |
Card |
结果和说明 | 圆角、阴影、颜色 |
LinearGradient |
结果背景 | 渐变渲染 |
Icon |
成功或失败状态 | 图标显示 |
Divider |
分析项分隔 | 线条显示 |
12.2 输入验证
OpenHarmony 上应重点验证:
- 输入框能正常输入英文和数字。
- 输入后结果立即刷新。
- 清空输入后结果卡片隐藏。
- 软键盘弹出后页面仍可滚动。
- 长文本不会撑破分析卡片。
12.3 示例验证
示例 Chip 要验证:
- 点击
radar后显示PALINDROME!。 - 点击
level后显示PALINDROME!。 - 点击
civic后显示PALINDROME!。 - 输入
hello后显示Not a Palindrome。 - 输入标点和空格后清洗结果正确。
12.4 边界验证
需要注意以下边界:
- 中文字符会被当前正则移除。
- 重音字符会被当前正则移除。
- emoji 会被当前正则移除。
- 空文本不是回文。
- 只输入标点时清洗结果为空,不显示结果卡片。
文本检测类工具的适配重点是规则一致性。只要清洗规则明确,结果就能解释;如果要支持多语言,需要扩展正则或引入更完整的文本处理方案。
十三、测试与验证
13.1 初始页面测试
Widget 测试可以验证首屏结构。
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('palindrome checker shows initial widgets', (tester) async {
await tester.pumpWidget(const PalindromeCheckerApp());
expect(find.text('Palindrome Checker'), findsWidgets);
expect(find.text('Try these examples:'), findsOneWidget);
expect(find.text('What is a Palindrome?'), findsOneWidget);
});
}
13.2 回文输入测试
testWidgets('detects palindrome text', (tester) async {
await tester.pumpWidget(const PalindromeCheckerApp());
await tester.enterText(find.byType(TextField), 'radar');
await tester.pump();
expect(find.text('PALINDROME!'), findsOneWidget);
expect(find.text('Matches exactly'), findsOneWidget);
});
13.3 非回文输入测试
testWidgets('detects non-palindrome text', (tester) async {
await tester.pumpWidget(const PalindromeCheckerApp());
await tester.enterText(find.byType(TextField), 'hello');
await tester.pump();
expect(find.text('Not a Palindrome'), findsOneWidget);
expect(find.text('Does not match'), findsOneWidget);
});
13.4 手工验证矩阵
| 场景 | 操作 | 预期 |
|---|---|---|
| 首次打开 | 启动应用 | 只显示输入、示例和说明 |
| 输入 radar | 输入 radar |
显示回文 |
| 输入 hello | 输入 hello |
显示非回文 |
| 输入带标点短语 | 输入 A man, a plan, a canal: Panama |
显示回文 |
| 点击示例 | 点击 level |
输入框填入并检测 |
| 输入标点 | 输入 !!! |
清洗为空,结果隐藏 |
| 输入中文 | 输入中文回文 | 当前规则会移除中文 |
十四、规则边界与实现解释
14.1 为什么只保留 a-z 和 0-9
源码使用的正则是:
RegExp(r'[^a-z0-9]')
它的含义是匹配所有不是英文小写字母和数字的字符,然后替换为空字符串。
14.2 为什么先转小写
text.toLowerCase()
这样 Radar、RADAR、radar 都会被统一为 radar。
14.3 为什么空文本不是回文
判断条件包含 cleaned.isNotEmpty。
cleaned.isNotEmpty && cleaned == reversed
如果清洗结果为空,即使空字符串等于反转空字符串,也不会被认为是回文。
14.4 为什么结果区按清洗文本显示
UI 使用 _processedText.isNotEmpty 判断是否展示结果。这样只输入空格或标点时不会显示误导结果。
十五、常见问题与优化建议
15.1 为什么中文回文不会生效
当前正则只保留英文小写字母和数字。
replaceAll(RegExp(r'[^a-z0-9]'), '')
中文字符会被移除,因此不能直接判断中文回文。
15.2 如何支持更多 Unicode 字符
可以使用更宽松的字符过滤规则,或按 Unicode 字符类别处理。
String normalizeForPalindrome(String input) {
return input.toLowerCase().replaceAll(RegExp(r'\s+'), '');
}
这只是保留更多字符的简单示例,完整多语言处理还要考虑组合字符、大小写和正规化。
15.3 为什么用 split 反转
当前反转方式简单直观。
cleaned.split('').reversed.join()
它适合 ASCII 字符。复杂 Unicode 字符串可能需要更谨慎的字符单元处理。
15.4 为什么只展示前 5 个示例
源码使用 take(5)。
_examplePalindromes.take(5)
这样示例 Chip 区域不会过长,页面保持简洁。
15.5 如何增加清空按钮
当前项目没有清空按钮。可以增加一个按钮清空输入和结果。
void _clear() {
_controller.clear();
setState(() {
_isPalindrome = null;
_processedText = '';
_reversedText = '';
});
}
这能让用户快速回到初始状态。
15.6 如何抽取纯函数
回文检测逻辑可以抽成纯函数。
bool isPalindromeAscii(String input) {
final cleaned = input.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '');
if (cleaned.isEmpty) return false;
return cleaned == cleaned.split('').reversed.join();
}
抽取后更容易写单元测试。
十六、工程扩展方向
16.1 建模检测结果
可以把检测结果封装成对象。
class PalindromeResult {
final String cleaned;
final String reversed;
final bool isPalindrome;
const PalindromeResult({
required this.cleaned,
required this.reversed,
required this.isPalindrome,
});
}
页面只负责展示 PalindromeResult。
16.2 增加历史记录
可以保存最近检测过的文本。
final List<PalindromeResult> history = [];
void addHistory(PalindromeResult result) {
history.insert(0, result);
if (history.length > 10) {
history.removeLast();
}
}
历史记录适合学习工具和文本练习场景。
16.3 增加多语言模式
可以提供模式切换:
- ASCII 英文数字模式。
- 保留所有非空白字符模式。
- 自定义正则模式。
enum PalindromeMode {
asciiAlphanumeric,
nonWhitespace,
custom,
}
这样用户可以根据文本语言选择判断规则。
16.4 增加逐字符对比
可以展示首尾字符对比过程。
class CharacterPair {
final String left;
final String right;
final bool matches;
const CharacterPair({
required this.left,
required this.right,
required this.matches,
});
}
这适合教学场景,能让回文判断更直观。
总结
palindrome_checker 是一个结构清晰的 Flutter 文本检测工具。它用 TextEditingController 管理输入,用 _checkPalindrome 完成小写转换、正则清洗、字符串反转和结果比较,用 _processedText、_reversedText 和 _isPalindrome 保存分析状态,再通过绿色或红色结果卡片、Analysis 分析卡片和说明卡片把判断过程展示给用户。
从 OpenHarmony 适配角度看,这个项目适合验证 Flutter 文本输入、示例 Chip、实时 onChanged、滚动布局、渐变卡片、图标状态和等宽文本展示。排查路径也很明确:结果不对看清洗正则,反转不对看 split('').reversed.join(),示例不生效看 _setExample,空输入显示异常看 _processedText.isNotEmpty。
掌握这个项目后,可以继续扩展清空按钮、历史记录、多语言回文规则、逐字符对比和检测结果建模,让回文检测器从简单 Demo 演进为更完整的跨平台文本分析工具。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!
相关资源:
更多推荐



所有评论(0)