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-z0-9,因此它适合英文和数字回文,不适合直接处理中文、重音字符或复杂 Unicode 文本。

一、项目背景与目标

1.1 项目定位

palindrome_checker 是一个轻量文本检测工具。用户可以直接输入文本,也可以点击示例 Chip 快速填入回文示例。应用会实时展示检测结果,并给出清洗后的文本、反转后的文本、有效字符数和匹配状态。

当前项目真实支持的功能包括:

  • 输入单词、短语或句子。
  • 输入变化时实时检测。
  • 点击示例 Chip 自动填入文本。
  • 将输入文本转为小写。
  • 移除非 a-z0-9 字符。
  • 对清洗后的文本执行反转。
  • 判断清洗文本和反转文本是否一致。
  • 空文本不显示结果卡片。
  • 回文结果展示绿色渐变卡片。
  • 非回文结果展示红色渐变卡片。
  • 分析区展示 Cleaned Text。
  • 分析区展示 Reversed Text。
  • 分析区展示 Character Count。
  • 分析区展示 Match Status。
  • 底部展示回文概念说明。

1.2 技术目标

本文围绕真实源码拆解以下内容:

  1. Flutter 应用入口和青色 Material 3 主题。
  2. _controller 如何管理输入框文本。
  3. _isPalindrome 为什么使用 bool?
  4. _processedText_reversedText 如何保存分析结果。
  5. _examplePalindromes 如何提供示例数据。
  6. _checkPalindrome 如何清洗、反转和比较。
  7. _setExample 如何填入示例并立即检测。
  8. 结果卡片如何根据检测结果切换颜色和图标。
  9. _buildAnalysisRow 如何复用分析行。
  10. 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 提供 MaterialAppScaffoldAppBarTextFieldActionChipCardIcon 等组件。

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()

因此用户能看到 radarlevelcivicrotormadam

六、输入框与示例 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]'), '')

该规则做了两件事:

  1. 把文本转为小写。
  2. 移除所有不是英文小写字母或数字的字符。

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 上应重点验证:

  1. 输入框能正常输入英文和数字。
  2. 输入后结果立即刷新。
  3. 清空输入后结果卡片隐藏。
  4. 软键盘弹出后页面仍可滚动。
  5. 长文本不会撑破分析卡片。

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()

这样 RadarRADARradar 都会被统一为 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 演进为更完整的跨平台文本分析工具。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源:

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐