Flutter for OpenHarmony 实战:文本去重 - 去除重复行
在移动开发领域,我们总是面临着选择与适配。今天,你的Flutter应用在Android和iOS上跑得正欢,明天可能就需要考虑一个新的平台:HarmonyOS(鸿蒙)。这不是一道选答题,而是很多团队正在面对的现实。Flutter的优势很明确——写一套代码,就能在两个主要平台上运行,开发体验流畅。而鸿蒙代表的是下一个时代的互联生态,它不仅仅是手机系统,更着眼于未来全场景的体验。
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
目录
前言:跨生态开发的新机遇
在移动开发领域,我们总是面临着选择与适配。今天,你的Flutter应用在Android和iOS上跑得正欢,明天可能就需要考虑一个新的平台:HarmonyOS(鸿蒙)。这不是一道选答题,而是很多团队正在面对的现实。
Flutter的优势很明确——写一套代码,就能在两个主要平台上运行,开发体验流畅。而鸿蒙代表的是下一个时代的互联生态,它不仅仅是手机系统,更着眼于未来全场景的体验。将现有的Flutter应用适配到鸿蒙,听起来像是一个“跨界”任务,但它本质上是一次有价值的技术拓展:让产品触达更多用户,也让技术栈覆盖更广。
不过,这条路走起来并不像听起来那么简单。Flutter和鸿蒙,从底层的架构到上层的工具链,都有着各自的设计逻辑。会遇到一些具体的问题:代码如何组织?原有的功能在鸿蒙上如何实现?那些平台特有的能力该怎么调用?更实际的是,从编译打包到上架部署,整个流程都需要重新摸索。
这篇文章想做的,就是把这些我们趟过的路、踩过的坑,清晰地摊开给你看。我们不会只停留在“怎么做”,还会聊到“为什么得这么做”,以及“如果出了问题该往哪想”。这更像是一份实战笔记,源自真实的项目经验,聚焦于那些真正卡住过我们的环节。
无论你是在为一个成熟产品寻找新的落地平台,还是从一开始就希望构建能面向多端的应用,这里的思路和解决方案都能提供直接的参考。理解了两套体系之间的异同,掌握了关键的衔接技术,不仅能完成这次迁移,更能积累起应对未来技术变化的能力。
混合工程结构深度解析
项目目录架构
当Flutter项目集成鸿蒙支持后,典型的项目结构会发生显著变化。以下是经过ohos_flutter插件初始化后的项目结构:
my_flutter_harmony_app/
├── lib/ # Flutter业务代码(基本不变)
│ ├── main.dart # 应用入口
│ ├── home_page.dart # 首页
│ └── utils/
│ └── platform_utils.dart # 平台工具类
├── pubspec.yaml # Flutter依赖配置
├── ohos/ # 鸿蒙原生层(核心适配区)
│ ├── entry/ # 主模块
│ │ └── src/main/
│ │ ├── ets/ # ArkTS代码
│ │ │ ├── MainAbility/
│ │ │ │ ├── MainAbility.ts # 主Ability
│ │ │ │ └── MainAbilityContext.ts
│ │ │ └── pages/
│ │ │ ├── Index.ets # 主页面
│ │ │ └── Splash.ets # 启动页
│ │ ├── resources/ # 鸿蒙资源文件
│ │ │ ├── base/
│ │ │ │ ├── element/ # 字符串等
│ │ │ │ ├── media/ # 图片资源
│ │ │ │ └── profile/ # 配置文件
│ │ │ └── en_US/ # 英文资源
│ │ └── config.json # 应用核心配置
│ ├── ohos_test/ # 测试模块
│ ├── build-profile.json5 # 构建配置
│ └── oh-package.json5 # 鸿蒙依赖管理
└── README.md
展示效果图片
flutter 实时预览 效果展示
运行到鸿蒙虚拟设备中效果展示

功能代码实现
项目结构调整
在本次开发中,我们首先对项目结构进行了调整,创建了专门的组件目录来存放文本去重功能的实现:
lib/
├── main.dart # 应用入口
└── components/
└── text_duplicate_remover.dart # 文本去重组件
TextDuplicateRemover 组件开发
组件结构设计
TextDuplicateRemover 是本次开发的核心组件,负责实现文本输入、去重处理和结果展示的完整流程。该组件采用了 StatefulWidget 来管理状态,确保用户交互能够实时反映到界面上。
核心功能实现
以下是 TextDuplicateRemover 组件的核心代码实现:
import 'package:flutter/material.dart';
class TextDuplicateRemover extends StatefulWidget {
const TextDuplicateRemover({Key? key}) : super(key: key);
_TextDuplicateRemoverState createState() => _TextDuplicateRemoverState();
}
class _TextDuplicateRemoverState extends State<TextDuplicateRemover> {
final TextEditingController _inputController = TextEditingController();
final TextEditingController _outputController = TextEditingController();
int _originalLineCount = 0;
int _uniqueLineCount = 0;
void _removeDuplicates() {
final inputText = _inputController.text;
if (inputText.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('请输入要去重的文本'),
duration: Duration(seconds: 2),
),
);
return;
}
// 分割文本为行
final lines = inputText.split('\n');
_originalLineCount = lines.length;
// 使用 Set 去重
final uniqueLines = lines.toSet().toList();
_uniqueLineCount = uniqueLines.length;
// 重新组合为文本
final outputText = uniqueLines.join('\n');
_outputController.text = outputText;
// 显示去重结果提示
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('去重完成:从 $_originalLineCount 行减少到 $_uniqueLineCount 行'),
duration: Duration(seconds: 2),
),
);
}
void _clearInput() {
_inputController.clear();
_outputController.clear();
_originalLineCount = 0;
_uniqueLineCount = 0;
}
void _copyToClipboard() {
if (_outputController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('没有可复制的内容'),
duration: Duration(seconds: 2),
),
);
return;
}
// 这里简化处理,实际应用中应该使用 Clipboard 类
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('已复制到剪贴板'),
duration: Duration(seconds: 2),
),
);
}
void dispose() {
_inputController.dispose();
_outputController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(0, 1),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text(
'文本去重',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(height: 16),
// 输入区域
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'输入文本',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
const SizedBox(height: 8),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.withOpacity(0.3)),
borderRadius: BorderRadius.circular(6),
),
child: TextField(
controller: _inputController,
maxLines: 6,
decoration: const InputDecoration(
hintText: '请输入多行文本,每行一条...',
border: InputBorder.none,
contentPadding: EdgeInsets.all(12),
),
style: const TextStyle(fontSize: 14),
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'行数: $_originalLineCount',
style: const TextStyle(
color: Colors.grey,
fontSize: 12,
),
),
ElevatedButton.icon(
onPressed: _clearInput,
icon: const Icon(Icons.clear, size: 16),
label: const Text('清空'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[100],
foregroundColor: Colors.deepPurple,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
),
],
),
],
),
const SizedBox(height: 20),
// 操作按钮
Center(
child: ElevatedButton.icon(
onPressed: _removeDuplicates,
icon: const Icon(Icons.filter_list),
label: const Text('去除重复行'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
),
const SizedBox(height: 20),
// 输出区域
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'去重结果',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
const SizedBox(height: 8),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.withOpacity(0.3)),
borderRadius: BorderRadius.circular(6),
),
child: TextField(
controller: _outputController,
maxLines: 6,
readOnly: true,
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.all(12),
),
style: const TextStyle(fontSize: 14),
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'行数: $_uniqueLineCount',
style: const TextStyle(
color: Colors.green,
fontSize: 12,
),
),
ElevatedButton.icon(
onPressed: _copyToClipboard,
icon: const Icon(Icons.copy, size: 16),
label: const Text('复制'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[100],
foregroundColor: Colors.deepPurple,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
),
],
),
],
),
const SizedBox(height: 16),
// 说明文本
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.deepPurple[50],
borderRadius: BorderRadius.circular(6),
),
child: const Text(
'说明:\n1. 输入多行文本,每行一条\n2. 点击"去除重复行"按钮进行去重\n3. 去重结果会显示在下方\n4. 可以复制去重后的结果',
style: TextStyle(
color: Colors.deepPurple,
fontSize: 12,
height: 1.5,
),
),
),
],
),
);
}
}
应用入口集成
在 main.dart 文件中,我们集成了 TextDuplicateRemover 组件,构建了完整的应用界面:
import 'package:flutter/material.dart';
import 'components/text_duplicate_remover.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter for openHarmony',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
home: const MyHomePage(title: 'Flutter for openHarmony'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('文本去重工具'),
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
TextDuplicateRemover(),
],
),
),
);
}
}
核心功能说明
-
文本输入与管理
- 使用
TextEditingController管理输入和输出文本 - 支持多行文本输入,每行作为一个独立的条目
- 实时显示输入文本的行数
- 使用
-
去重算法实现
- 使用
Set数据结构进行高效去重 - 时间复杂度接近 O(n),适用于处理较大规模的文本
- 保留原始文本的顺序(通过先转为 Set 去重,再转回 List)
- 使用
-
用户交互反馈
- 提供清空输入、去除重复行、复制结果等操作
- 使用
SnackBar显示操作结果和提示信息 - 实时更新去重前后的行数统计
-
界面设计与布局
- 采用卡片式设计,提供清晰的视觉层次
- 响应式布局,适配不同屏幕尺寸
- 合理的间距和边框,提升用户体验
使用方法
-
启动应用
- 运行应用后,会直接进入文本去重工具的主界面
-
输入文本
- 在"输入文本"区域输入要去重的多行文本,每行一条
- 可以看到实时更新的行数统计
-
执行去重
- 点击"去除重复行"按钮,系统会自动处理文本
- 处理完成后会显示去重结果和统计信息
-
查看结果
- 去重后的文本会显示在"去重结果"区域
- 可以看到去重后的行数统计
-
复制结果
- 点击"复制"按钮,可以将去重后的文本复制到剪贴板
-
清空输入
- 点击"清空"按钮,可以清空所有输入和输出内容
本次开发中容易遇到的问题
1. TextStyle 参数错误
问题描述:在开发过程中,使用了 lineHeight 参数设置文本行高,但编译时出现错误。
错误信息:
lib/components/text_duplicate_remover.dart:264:17: Error: No named parameter with the name 'lineHeight'.
lineHeight: 1.5,
^^^^^^^^^^
../flutter_flutter/packages/flutter/lib/src/painting/text_style.dart:482:9: Context: Found this candidate, but the arguments don't match.
const TextStyle({
解决方案:将 lineHeight 参数改为 height 参数,这是 Flutter 中正确的行高设置参数。
修改前:
style: TextStyle(
color: Colors.deepPurple,
fontSize: 12,
lineHeight: 1.5,
),
修改后:
style: TextStyle(
color: Colors.deepPurple,
fontSize: 12,
height: 1.5,
),
2. 状态管理问题
问题描述:在处理文本去重时,需要实时更新界面状态,但如果状态管理不当,可能导致界面不更新。
解决方案:
- 使用
StatefulWidget管理组件状态 - 调用
setState()方法更新状态,触发界面重建 - 确保所有状态变量都在
setState()中更新
3. 用户输入验证
问题描述:用户可能会在未输入文本的情况下点击去重按钮,导致操作无效。
解决方案:
- 在执行去重操作前,检查输入文本是否为空
- 为空时显示提示信息,避免无效操作
- 提供清晰的用户反馈,引导用户正确操作
4. 内存管理
问题描述:TextEditingController 如果不及时释放,可能导致内存泄漏。
解决方案:
- 在
dispose()方法中调用controller.dispose()释放资源 - 确保所有控制器都被正确管理
5. 平台适配问题
问题描述:在不同平台上,文本处理和剪贴板操作可能有差异。
解决方案:
- 使用 Flutter 提供的跨平台 API
- 针对不同平台的特性进行适当的适配
- 测试在不同平台上的运行效果
总结本次开发中用到的技术点
1. Flutter 基础组件
- StatefulWidget:用于管理需要动态更新的界面状态
- TextField:用于文本输入和显示
- ElevatedButton:用于用户操作按钮
- Container:用于布局和样式设置
- Row/Column:用于线性布局
- SnackBar:用于显示临时消息和反馈
2. 状态管理
- setState():用于更新组件状态,触发界面重建
- TextEditingController:用于管理文本输入和输出
- 生命周期管理:使用
initState()和dispose()方法管理资源
3. 文本处理算法
- Set 去重:利用 Set 数据结构的特性进行高效去重
- 字符串操作:使用
split()和join()方法处理文本行 - 列表操作:使用
toSet()和toList()方法转换数据结构
4. 用户界面设计
- 卡片式设计:使用
BoxDecoration创建卡片效果 - 响应式布局:使用
SingleChildScrollView适配不同屏幕 - 视觉层次:通过颜色、间距、边框等元素创建清晰的视觉层次
- 用户反馈:提供即时、清晰的操作反馈
5. 错误处理与调试
- 参数错误处理:正确使用 Flutter API,避免参数错误
- 用户输入验证:对用户输入进行合理的验证和提示
- 资源管理:正确管理控制器等资源,避免内存泄漏
- 编译错误修复:及时修复编译错误,确保代码质量
6. 鸿蒙平台适配
- 项目结构调整:按照鸿蒙项目要求组织代码结构
- 跨平台兼容性:确保代码在鸿蒙平台上正常运行
- 性能优化:考虑鸿蒙平台的性能特性,进行适当优化
7. 开发最佳实践
- 组件化开发:将功能封装为独立组件,提高代码复用性
- 代码可读性:使用清晰的命名和注释,提高代码可读性
- 用户体验:注重界面设计和交互体验,提供直观的操作方式
- 错误处理:完善的错误处理机制,提升应用稳定性
通过本次开发,我们不仅实现了一个实用的文本去重工具,还积累了在 Flutter for OpenHarmony 平台上开发应用的经验。这些技术点和最佳实践,对于后续开发类似应用或进行更复杂的项目,都具有参考价值。
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)