1. 项目概述与技术选型思路
1.1 这个项目到底在做什么
先说实话,第一次听到“便单服务类”这个叫法的时候,我也愣了一下。翻译成人话,其实就是便签/笔记类应用服务——用户随手记一条想法、列一个待办清单、存一句灵感,然后下次打开还能找到。这个品类在应用商店里看起来简单,真正上手做才发现坑不少:数据怎么存才不丢、怎么跨页面同步、如何在系统资源有限的场景下保证流畅,每一项都牵扯到具体的架构决策。
「便笺星(NoteStar)」是我用 Flutter 作为跨端框架、跑在 HarmonyOS 6.0 上的一个真实项目。选这个组合的原因很简单:鸿蒙现在的用户量已经不小了,但纯 ArkTS 开发的学习成本和生态迁移成本对很多团队来说还是偏高。Flutter 的渲染引擎和 Dart 语言本身是中立的,通过鸿蒙提供的 Flutter 适配层可以直接把现有的 Flutter 代码跑在鸿蒙设备上,这就让“一套代码、多端运行”从口号变成了可行的工程路径。
这个项目适合谁参考?如果你正在做跨平台应用、需要对鸿蒙设备做兼容、或者只是想看看 Flutter 在非 Android/iOS 平台上的真实表现,那这篇文章值得你花几分钟读完。我会把选型逻辑、持久化方案、核心功能拆解和调试记录都放出来,能直接抄作业的地方绝不含糊。
1.2 为什么选 Flutter 而不是直接写 ArkTS
这个问题我当初纠结了很久。鸿蒙 6.0 的 ArkTS 已经相当成熟,用原生方式开发肯定是最“正统”的路线。但我最终选了 Flutter,核心理由有三条。
第一,代码复用率。团队手里已经有一套基于 Flutter 的便签应用,如果换 ArkTS 重写,工作量不是翻倍,是直接翻三倍——UI 层、业务层、数据层全部推翻。而用 Flutter 的鸿蒙适配层,UI 代码和业务逻辑基本可以原封不动搬过来,只需要针对鸿蒙的系统能力做一层薄薄的适配。
第二,Flutter 的渲染管线在鸿蒙上表现并不差。很多人以为 Flutter 只能跑 Android 和 iOS,实际上鸿蒙官方社区和 OpenHarmony 生态很早就开始了 Flutter 引擎的移植。到 HarmonyOS 6.0 这个节点,Flutter 应用的帧率、内存占用、启动耗时都已经到了可用的水平。我在 NoteStar 里做了简单的压测,普通的列表滑动和页面切换基本稳定在 60 帧。
第三,生态和招聘。Flutter 的开发者基数比 ArkTS 大得多,遇到问题搜索引擎一抓一大把。用 Flutter 意味着以后想适配其他平台(比如 Windows、macOS)也有现成的路子。
当然,选 Flutter 不是没有代价。鸿蒙有一些独特的能力,比如原子化服务、分布式数据流转,这些在 Flutter 里不能直接调用,必须通过 platform channel 桥接到鸿蒙原生侧。这个部分的开发难度比纯 ArkTS 高一些,后面我会专门说这块怎么处理。
提示:如果你是个人开发者、小团队,或者核心诉求是“尽快把业务跑起来”,Flutter + 鸿蒙适配层的方案性价比很高。如果你要做深度的系统级应用、大量依赖鸿蒙分布式特性,那还是老老实实写 ArkTS。
1.3 便笺类应用的核心难点拆解
便笺应用看起来简单,实际上它是一个非常典型的“轻交互、重数据”类应用。轻交互是说界面逻辑不复杂,无非是列表、编辑页、搜索、设置几个页面;重数据是说所有价值都在用户沉淀的笔记内容上,数据一旦丢失,用户对产品的信任就瞬间归零。
所以 NoteStar 的核心难点可以拆成三块:
- 持久化策略:数据存哪里、怎么存、怎么保证不丢、怎么在应用被杀掉之后还能恢复;
- 数据组织:便笺不是一堆孤立字符串,它需要支持分类、标签、置顶、归档、全文搜索这些操作,所以底层的存储模型必须有扩展性;
- 性能体验:用户打开应用的第一诉求是“快速记一笔”,如果冷启动要 3 秒、列表滑动掉帧、搜索要转圈,那不管功能多全,留存率都不会好看。
这三点听起来是常识,但实际落地时每一步都有细节。接下来我从持久化方案讲起,这是整个项目的基石。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 持久化存储方案剖析与落地设计
2.1 持久化技术选型:轻量偏好 + 关系型数据库双层架构
持久化存储这个事,很多人第一反应是“直接上 SQLite”。这没有错,但对于便笺应用来说,如果所有数据都塞进数据库,反而会带来不必要的复杂性和性能开销。我的方案是做一个双层架构:
上层是 SharedPreferences(轻量键值对),用来存用户偏好、设置项、上次打开的页面索引、主题颜色这类轻量数据。这类数据的特点是单条体积小、读写频率低、不需要复杂查询,用键值对最合适。Flutter 侧对应的是 shared_preferences 插件,在鸿蒙上也提供对应实现,可以直接用。
下层是关系型数据库(SQLite/Drift),用来存便笺的核心数据。每一张便笺包含标题、正文、创建时间、修改时间、分类、标签、置顶状态、归档状态等结构化字段。数据库的查询能力强,可以支持 WHERE 过滤、ORDER BY 排序、LIKE 搜索,这些都是便笺列表页和搜索页的基础。
两层之间通过一个仓储层(Repository)统一暴露接口,上层页面不关心数据到底存在哪里,只需要调用 getNotes()、saveNote(note)、deleteNote(id) 这样的方法。这种设计的最大好处是后期如果要换存储引擎(比如换到鸿蒙原生的 relationalStore),只需要重写仓储层的实现,页面代码一行都不用动。
2.2 数据表设计与索引优化
便笺表我命名为 note,字段设计如下:
dart复制class Note {
int id; // 主键,自增
String title; // 标题
String content; // 正文内容
int categoryId; // 分类ID,外键关联 category 表
List<String> tags; // 标签列表,存储为 JSON 字符串
bool isTop; // 是否置顶
bool isArchive; // 是否归档
int createdAt; // 创建时间戳(毫秒)
int updatedAt; // 最后修改时间戳(毫秒)
int? remindAt; // 提醒时间戳(毫秒),可为空
}
对应的建表 SQL 我放在这里,字段注释写清楚了:
sql复制CREATE TABLE note (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL DEFAULT '',
content TEXT NOT NULL DEFAULT '',
category_id INTEGER NOT NULL DEFAULT 1,
tags TEXT NOT NULL DEFAULT '[]',
is_top INTEGER NOT NULL DEFAULT 0,
is_archive INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
remind_at INTEGER
);
CREATE INDEX idx_note_updated_at ON note(updated_at DESC);
CREATE INDEX idx_note_category ON note(category_id, is_archive);
CREATE INDEX idx_note_remind_at ON note(remind_at);
索引不是随便建的。updated_at 是列表页默认排序字段,必须加索引;category_id + is_archive 的联合索引用来加速分类筛选和归档过滤;remind_at 索引用于定时扫描“哪些便笺需要触发提醒”的任务。这三个索引覆盖了 NoteStar 95% 以上的查询路径,实测下来列表查询从全表扫描的几十毫秒级降到了个位数毫秒级。
2.3 数据库连接与 CRUD 实现细节
数据库层我用的是 Drift(原 moor)这个 Flutter 持久化库,底层仍然是 SQLite,但它提供了类型安全的查询API和响应式数据流,用起来比直接拼 SQL 字符串舒服很多。鸿蒙上跑 Drift 没有大的障碍,SQLite 引擎通过 FFI 装载,跟 Android 上基本一致。
初始化连接的代码如下:
dart复制// database.dart
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
part 'database.g.dart';
@DriftDatabase(tables: [Notes, Categories])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
static QueryExecutor _openConnection() {
return NativeDatabase.createInBackground(
File('note_star.db'),
// 在鸿蒙上,数据库文件需要放到应用私有目录
);
}
}
这里有个鸿蒙适配的小知识点:文件路径不能像 Android 那样直接用 getDatabasesPath(),需要桥接到鸿蒙侧获取应用的沙箱目录。我在 Flutter 侧通过 platform channel 调用鸿蒙的 context.filesDir 拿到私有目录,再把路径传给数据库初始化逻辑,这样保证了数据库文件落在系统允许的应用私有区域内,符合规范也安全。
CRUD 这些基础操作我就不贴完整代码了,说几个关键点:
- 插入和更新要设置正确的时间戳。创建时同时写
created_at和updated_at;更新时只改updated_at,这能保证排序逻辑正确。 - 删除不要物理删。我加了一个
deleted_at字段,删除时打标记,这样用户如果在设置里开“最近删除”,还能找回。真正的物理清理由数据清理任务在后台统一执行。 - 批量操作要用事务包裹,不然大量置顶/归档/删除操作时,性能会差很多。
2.4 备份与恢复:本地 JSON 导出与导入
数据安全不能只靠数据库。用户手机可能丢了、应用可能被清除数据,所以 NoteStar 里我做了一个完整的备份/恢复能力:把所有便笺序列化成 JSON 文件,导出到用户指定位置;恢复时反向操作,把 JSON 文件里的数据写回数据库。
导出格式大致如下:
json复制{
"app": "NoteStar",
"version": 1,
"exportedAt": 1743300000000,
"notes": [
{
"id": 1,
"title": "买牛奶",
"content": "记得买脱脂的",
"categoryId": 2,
"tags": ["购物"],
"isTop": false,
"isArchive": false,
"createdAt": 1743290000000,
"updatedAt": 1743291000000
}
]
}
导出时需要注意一个细节:ID 不能直接透传。因为恢复目标设备上可能已经存在相同 ID 的便笺,所以导入时我会把 ID 置空重新自增,用 updatedAt 排序恢复顺序。导出文件不加密,因为本地导出本质上还是用户自己的数据,加了加密反而增加了用户自行查看和转移的成本。
3. 便笺核心功能实现与交互细节
3.1 便笺列表与页面状态管理架构
便笺列表是用户打开应用后的第一屏,它的质量决定了用户对 App 的第一印象。NoteStar 的列表做了两级筛选:顶部分类标签栏(全部、工作、生活、灵感、购物),下面叠加搜索框。列表项展示标题、摘要(正文前 40 个字)、标签、修改时间和置顶标识。
数据层我用 Stream 方式来驱动 UI。Drift 自带响应式查询能力,数据库表的任何变化都会自动推送到 UI 层,省去了手动刷新列表的麻烦。UI 层用 StreamBuilder 监听查询结果:
dart复制StreamBuilder<List<Note>>(
stream: _noteRepository.watchVisibleNotes(
categoryId: _selectedCategoryId,
keyword: _keyword,
),
builder: (context, snapshot) {
final notes = snapshot.data ?? [];
return ListView.separated(
itemCount: notes.length,
itemBuilder: (context, index) => NoteListItem(note: notes[index]),
separatorBuilder: (context, index) => const Divider(height: 1),
);
},
)
页面状态管理我选的是 Provider + ChangeNotifier。没有上 Riverpod 或者 Bloc,因为 NoteStar 的页面间状态共享不算复杂,Provider 足够清晰。记住一个原则:状态管理的复杂度要和业务复杂度匹配,杀鸡不用牛刀。
3.2 新建与编辑页:自动保存防丢失
编辑页是用户最频繁操作的页面,我的设计核心是“自动保存 + 防丢失”。用户每敲一个字,就自动更新到数据库,不需要点保存按钮。这个策略看起来简单,但真正的难点在于“什么时候触发保存”和“怎么避免频繁写库”。
我的做法是:监听输入框的 onChanged,但通过一个 500ms 的防抖(debounce)来合并短时间内的多次输入。也就是说,用户连续打字时不会每次都写库,等停顿超过 500ms 才落一次库。这样既保证了数据安全,又不会因为频繁 IO 导致卡顿。Dart 代码实现很简单:
dart复制Timer? _debounce;
void _onContentChanged(String value) {
_debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
_repository.updateNoteContent(_noteId, value);
});
}
页面销毁时还有一道兜底:在 dispose() 里取消定时器并立即保存一次当前内容,确保用户退出编辑页时数据一定在库里。
注意:自动保存虽然方便,但会有“误删内容无法撤销”的风险。NoteStar 的做法是在数据库里保留一份编辑历史表,每次保存时把旧的完整内容快照存下来,用户可以在编辑页的菜单里回退到任意历史版本。这个功能做起来并不复杂,但对用户体验提升巨大。
3.3 分类、标签与置顶:数据组织能力的核心
便笺如果只是平铺的列表,数量一多就没法用了。NoteStar 做了三层组织维度:
分类是用户自定义的一组目录,一个便笺只能属于一个分类。这个对应 note.category_id 外键。分类管理页支持增删改,删除分类时需要处理一个边界情况:把该分类下的便笺全部移到默认分类,而不是直接删掉用户数据。
标签是更灵活的组织方式,一个便笺可以有多个标签,相当于给便笺打多个标记。标签用 JSON 数组存字符串,查询时用 SQLite 的 LIKE 匹配。这个方案对标签数量不大的场景完全够用,但如果未来要做基于标签的大规模聚合统计,可以考虑把标签拆成关联表,现在先不过度设计。
置顶是很多便笺应用都会有的功能,用户把重要便笺固定到列表最前面。实现上就是在 SELECT 时先按 is_top 降序、再按 updated_at 降序排列,但有个体验上的细节:置顶的便笺内部也要有排序,不然多个置顶之间的顺序就会固定不变,用户会觉得“怎么拖不动”。NoteStar 里置顶便笺额外记录一个 topOrder 字段,用户长按拖拽修改顺序。
3.4 全文搜索与低延时体验
全文搜索我一开始用了 SQLite 的 LIKE '%keyword%',数据量小的时候没问题,但我测试了 3000 条便笺数据后,搜索响应时间已经到了 500ms 以上,体验明显发肉。
后来做了一次升级:在 SQLite 的基础上增加 FTS4 全文搜索虚拟表。FTS4 是 SQLite 内置的全文搜索方案,支持中文分词和 BM25 排序算法。NoteStar 在便笺写入时同步更新 FTS 索引表,搜索时直接查 FTS 表,响应时间从 500ms 降到了 20ms 以内,效果非常明显。
建表和维护代码如下:
sql复制CREATE VIRTUAL TABLE note_fts USING fts4(content, title, tags);
-- 写入时同步更新索引
INSERT INTO note_fts(rowid, content, title, tags)
VALUES (1, '记得买脱脂牛奶', '买牛奶', '购物');
-- 查询时关联原表
SELECT n.* FROM note n
JOIN note_fts f ON n.id = f.rowid
WHERE note_fts MATCH '牛奶*'
ORDER BY rank;
这个优化给我一个很大的提醒:很多“简单”功能在数据量小的时候一切正常,一旦规模化就会出现性能拐点。做架构设计时,一定要提前想清楚数据规模的上限,预留优化空间。
4. 关键实操过程与鸿蒙适配实录
4.1 开发环境搭建与鸿蒙 Flutter 工程配置
整个项目我是在 Windows 11 上开发的,配合鸿蒙官方的 DevEco Studio 和 Flutter SDK。环境搭建的步骤如下:
- 安装 DevEco Studio,版本选用支持 HarmonyOS 6.0 的 5.x 及以上;
- 安装 Flutter SDK(3.22+,我用的 3.24 的稳定版);
- 安装鸿蒙 Flutter 适配的 SDK 包,把鸿蒙平台的 toolchain 配到 Flutter 中;
- 在 DevEco Studio 中创建一个 Java 或 ArkTS 壳工程,作为鸿蒙应用的入口;
- 通过命令行把 Flutter 工程构建成鸿蒙的 hap 包,集成到壳工程中。
具体的命令我这里贴出来:
bash复制# 初始化 Flutter 工程
flutter create note_star
# 添加鸿蒙平台支持
flutter pub add flutter_ohos
# 构建鸿蒙产物
flutter build hap --release
构建完成后,在 DevEco Studio 里刷新工程,然后把 entry 模块指向构建出的 Flutter 产物,就能在鸿蒙模拟器或真机上运行了。这里的坑主要是版本匹配——Flutter SDK、flutter_ohos 适配包、DevEco Studio 三者的版本必须严格对齐,差一个版本都可能报编译错误。
4.2 鸿蒙权限与沙箱目录适配
鸿蒙 6.0 的权限模型和 Android 类似,但细节差异不小。NoteStar 用到的权限包括:存储权限(读写备份文件)、通知权限(提醒功能)。申请权限的方式是通过鸿蒙的 abilityAccessCtrl 模块,在 module.json5 里声明:
json复制{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.WRITE_IMAGEVIDEO",
"reason": "用于导出便笺备份文件",
"usedScene": {
"abilities": ["EntryAbility"]
}
},
{
"name": "ohos.permission.NOTIFICATION_CONTROLLER",
"reason": "用于发送便笺提醒通知",
"usedScene": {
"abilities": ["EntryAbility"]
}
}
]
}
}
权限申请在用户侧是弹窗确认,这一点和 Android 一致。但接口函数名不同,需要在鸿蒙原生侧封装 adapter,暴露给 Flutter 调用。我封装了一个 PermissionService,通过 MethodChannel 调用。
沙箱目录也值得一提。鸿蒙对应用数据目录有严格隔离,Flutter 默认拿不到可以持久化写入的路径。我的经验是统一走鸿蒙侧获取:
dart复制static Future<String> getAppDir() async {
const channel = MethodChannel('note_star/system');
return await channel.invokeMethod('getAppDir');
}
鸿蒙原生侧用 this.context.filesDir 返回路径,转成字符串传回 Flutter,后续所有文件读写都基于这个路径。
4.3 本地提醒功能的实现与踩坑记录
便笺提醒功能是用户粘性的一大来源。用户给某个便笺设置了提醒时间,到点之后应用要弹出系统通知。这个功能在 Android 上有现成的 flutter_local_notifications 插件,但在鸿蒙上就不那么顺了。
我踩的坑主要是:通知渠道不同。鸿蒙的通知体系有自己的 NotificationRequest 和渠道概念,Flutter 插件默认走的是 Android 渠道模型,在鸿蒙上直接就懵了。
最后我选择了平台通道的方案:Flutter 侧把提醒时间传给鸿蒙原生侧,由鸿蒙的提醒代理(reminderAgentManager)来管理和发布通知。代码大致如下:
kotlin复制// HarmonyOS 原生侧
import ohos.r would.eventhandler.*;
import ohos.reminderagent.ReminderAgentManager;
val reminderRequest = ReminderRequest(
reminderTime = timestampMillis,
title = noteTitle,
content = noteContent
)
ReminderAgentManager.registerReminder(reminderRequest)
这个方案的好处是,即使用户把 App 杀掉了,系统级的提醒代理依然能按时触发通知,这是 Flutter 纯前台方案做不到的。坏处是要维护一份鸿蒙原生代码。但为了体验,值得。
4.4 数据库迁移与版本升级策略
应用一旦发布,数据库就是个“只准向前走”的东西。用户已经装了旧版本、库里存了数据,你新版本改了表结构,绝对不能直接把表删了重建。Drift 提供了一套迁移机制,我在 schemaVersion 递增的同时配置了 migration:
dart复制@override
MigrationStrategy get migration {
return MigrationStrategy(
onCreate: (m) async {
await m.createAll();
},
onUpgrade: (m, from, to) async {
if (from < 2) {
await m.addColumn(notes, notes.remindAt);
}
},
);
}
核心原则是:每个版本的升级脚本要写清楚“从哪个版本到哪个版本做了什么变更”,并且升级前最好做好备份。我在内部测试时遇到过一次误操作导致升级后数据丢失,从那以后每次发布前都会用测试数据走一遍完整的升级流程。
5. 常见问题与调试技巧实录
5.1 鸿蒙真机调试时常见的 5 个问题
| 现象 | 根本原因 | 解决方式 |
|---|---|---|
| Flutter 工程构建报 CMake 或 Gradle 错误 | flutter_ohos 与 Flutter SDK 版本不匹配 | 统一升级到官网推荐的版本组合,不要混搭 |
| 运行到真机后白屏 | 鸿蒙壳工程没有正确引导 Flutter 入口 | 检查 module.json5 和 EntryAbility 的配置,确保 hap 包中 Flutter 产物已正确打包 |
| 数据库文件无法创建 | 路径不是应用沙箱目录 | 通过鸿蒙侧 context.filesDir 获取路径,不要硬编码 /data 之类 |
| 通知不弹出 | 通知权限未申请,或提醒代理未注册 | 动态向用户请求通知权限,注册提醒代理并在 onDestroy 里释放 |
| 热重载失效 | 鸿蒙真机对 hot restart 支持不完整 | 修改原生代码或者改了 platform channel 后,手动重新构建,不要依赖热重载 |
5.2 Flutter 底部弹窗输入框被键盘遮挡
这个问题的现象是:页面底部弹出一个填写便笺标题的对话框,但点击输入框时键盘弹出来,把整个弹窗顶得变了形。
排查后发现是 Scaffold 的 resizeToAvoidBottomInset 属性默认开启导致的,弹窗被压缩了。解决办法也简单,在该页面里把 resizeToAvoidBottomInset 设置为 false,然后手动设置弹窗的 padding,确保输入框在键盘上方即可。
dart复制Scaffold(
resizeToAvoidBottomInset: false,
body: ...,
)
这个坑在 Android 上不常见,但在鸿蒙的窗口管理模型下表现得更明显。如果遇到类似问题,可以先用这个开关做个快速验证,再根据实际情况调布局。
5.3 持久化数据丢失的深度排查
有用户反馈说,偶尔会出现便笺写完后过几个小时再看就消失了。这个 bug 排查了好久,最后发现是两个原因叠加的:
第一,自动保存的防抖逻辑有问题。页面销毁时,dispose() 里取到的 _noteId 已经是 null(因为用户新建便笺还没写入数据库拿到 ID),“立即保存”路径自动跳过了,导致这次输入的内容直接丢失。修复方式是:在新建便笺时,先插入一条空记录拿到 ID,再让用户开始输入,这样任何后续保存都有了一个唯一 ID。
第二,数据库事务的冲突处理不当。用户在编辑页连续快速修改时,前一个异步写库操作还没结束,后一个已经提交了,导致更新的数据覆盖了旧数据。修复方式是:所有写操作通过一个串行的任务队列提交,确保数据库操作不会并发。
这两个问题看起来都挺低级,但实际环境里真的不容易发现。把这两个修复上线后,再也没收到过数据丢失的反馈。
5.4 性能优化:列表滑动与内存占用
Flutter 的 ListView.builder 本身是懒加载的,但列表项内部的渲染如果做得重,还是会卡。NoteStar 做了几项优化:
- 列表项的富文本预览使用
Text组件的maxLines: 2+overflow: TextOverflow.ellipsis,避免展开完整正文导致长文本绘制开销; - 标签
Chip使用RepaintBoundary包裹,避免滑动时整行重建; - 图片(如果便笺里插入图片)用缩略图加载,而不是原图直接塞进列表。
实测数据:3000 条便笺的列表,冷启动到完全可交互耗时约 1.8 秒,滑动帧率稳定在 60 帧(鸿蒙模拟器略低,真机没问题)。对于这种体量的应用,这个数据我认为是合格的。
6. 项目后续扩展与个人经验总结
6.1 数据云端同步的演进方向
NoteStar 目前的持久化是纯本地方案,这是第一版为了快速落地而做的取舍。但本地数据最大的风险是单点故障——手机丢了、系统崩溃、误操作恢复出厂,数据就没了。
下一步我计划引入云同步能力。技术路线有两种选择:一是接第三方云数据库(如 Firebase 或国内的云数据库服务),二是通过鸿蒙的分布式数据能力做设备间同步。第一种生态成熟、接入快,第二种更原生、对鸿蒙多设备协同更友好,但需要更深地绑定鸿蒙体系。如果让我推荐,个人开发者先走第一条路,快速验证同步体验;团队资源充裕再评估第二种。
同步方案里最核心的坑是冲突解决。多设备同时编辑同一张便笺,怎么合并?我的思路是每个便笺维护一个 version 字段(单调递增),同步时以高版本为准;如果内容同时被两边修改,则保留两个版本并标记为冲突,由用户手动选择保留哪份。这个方案虽然朴素,但对便笺这种低并发场景完全够用。
6.2 从 NoteStar 中沉淀出的通用方法论
做完这个项目,最大的体会不是某个技术点有多难,而是“架构决策的优先级要清楚”。便笺类应用的本质是用户数据的可靠存储和快速访问,所以持久化层是地基,UI 再好看、动画再华丽,数据存不好一切白搭。
其次,跨平台开发一定要对目标平台的原生能力有敬畏心。Flutter 帮你跨了 UI 层,但系统能力(通知、权限、沙箱)还是得跟原生打交道。我建议在项目早期就列一个“原生能力清单”,把需要跟系统交互的点提前摸清楚,不要到后期才临时抱佛脚。
最后想说的是:我们在社区里经常看到“为什么谷歌放弃了 Flutter”之类的讨论,好像 Flutter 热度一降就要凉。但从实际项目来看,Flutter 的开发效率和生态能力依然是跨端方案里的第一梯队。它不是一个“最好”的方案,但它是“够好”且“可靠”的方案。NoteStar 能在较短时间内同时覆盖鸿蒙和 Android 两端,就是最直接的佐证。
如果你也在做类似的项目,或者在 Flutter + 鸿蒙适配的路上踩了坑,欢迎留言交流。这个领域还在快速演进,大家都在摸索,经验和教训都需要互相打补丁。
