1. 项目概述:Flutter+OpenHarmony轻量级记事本开发实战
去年在开发跨平台应用时,我偶然发现Flutter对OpenHarmony的支持已经趋于成熟。作为一个常年使用Flutter的开发者,我决定尝试用这个组合开发一个轻量级记事本应用,重点实现"最近编辑"这个核心功能。这个项目最吸引我的地方在于,它验证了Flutter在国产操作系统上的完整开发流程,从环境搭建到功能实现再到性能优化,整个过程充满了技术探索的乐趣。
这个实战项目适合以下几类开发者参考:
- 想要尝试Flutter跨平台开发但苦于没有合适项目的入门者
- 对OpenHarmony生态感兴趣但不知从何入手的技术探索者
- 需要快速开发轻量级记事本类应用的实践派程序员
整个项目采用纯Dart语言开发,UI层使用Flutter标准组件,数据持久化采用sqflite插件,最终打包为OpenHarmony应用。下面我就从环境准备开始,详细拆解这个项目的完整实现过程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境配置与项目初始化
2.1 基础环境搭建
在开始之前,我们需要准备以下开发环境:
- Flutter SDK(建议3.0以上版本)
- OpenHarmony SDK
- DevEco Studio(用于最终打包)
- Java JDK(建议11+版本)
重要提示:OpenHarmony目前对Flutter的支持主要通过OHOS渠道实现,需要特别配置flutter工具的OHOS支持。我在实际操作中发现,直接使用官方main分支的Flutter SDK可能会出现兼容性问题,建议使用openharmony分支的定制版本。
配置环境变量的关键步骤:
bash复制# 添加Flutter到PATH
export PATH="$PATH:`pwd`/flutter/bin"
# 设置OpenHarmony工具链
export OHOS_HOME=/path/to/openharmony/sdk
export PATH="$PATH:$OHOS_HOME/toolchains"
2.2 项目创建与基础配置
使用以下命令创建Flutter项目:
bash复制flutter create --template=app --platforms=android,ios,ohos recent_notes
项目创建完成后,需要在pubspec.yaml中添加必要的依赖:
yaml复制dependencies:
flutter:
sdk: flutter
sqflite: ^2.2.0+4
path_provider: ^2.0.15
intl: ^0.18.1
特别需要注意的是,要为OpenHarmony添加特定的构建支持,需要在项目根目录创建ohos_config.json:
json复制{
"apiVersion": 7,
"displayName": "RecentNotes",
"bundleName": "com.example.recentnotes",
"vendor": "example",
"versionCode": 1,
"versionName": "1.0.0",
"minAPIVersion": 7,
"targetAPIVersion": 8,
"pages": "$profile:main_pages"
}
3. 核心功能实现详解
3.1 数据模型设计与数据库操作
记事本应用的核心是笔记数据的存储与管理。我们设计了一个简单的Note模型:
dart复制class Note {
int? id;
String title;
String content;
DateTime lastEdited;
Note({
this.id,
required this.title,
required this.content,
required this.lastEdited,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'content': content,
'last_edited': lastEdited.millisecondsSinceEpoch,
};
}
factory Note.fromMap(Map<String, dynamic> map) {
return Note(
id: map['id'],
title: map['title'],
content: map['content'],
lastEdited: DateTime.fromMillisecondsSinceEpoch(map['last_edited']),
);
}
}
数据库操作类封装了CRUD方法,特别关注lastEdited字段的更新:
dart复制class NotesDatabase {
static final NotesDatabase instance = NotesDatabase._init();
static Database? _database;
NotesDatabase._init();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB('notes.db');
return _database!;
}
Future<Database> _initDB(String filePath) async {
final dbPath = await getApplicationDocumentsDirectory();
final path = join(dbPath.path, filePath);
return await openDatabase(
path,
version: 1,
onCreate: _createDB,
);
}
Future _createDB(Database db, int version) async {
await db.execute('''
CREATE TABLE notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
last_edited INTEGER NOT NULL
)
''');
}
Future<int> createNote(Note note) async {
final db = await instance.database;
return await db.insert('notes', note.toMap());
}
Future<List<Note>> readAllNotes() async {
final db = await instance.database;
final result = await db.query(
'notes',
orderBy: 'last_edited DESC',
);
return result.map((json) => Note.fromMap(json)).toList();
}
Future<int> updateNote(Note note) async {
final db = await instance.database;
note.lastEdited = DateTime.now();
return await db.update(
'notes',
note.toMap(),
where: 'id = ?',
whereArgs: [note.id],
);
}
}
3.2 最近编辑功能实现
"最近编辑"功能的核心在于:
- 每次编辑笔记时更新lastEdited时间戳
- 查询时按lastEdited降序排列
- 在UI层突出显示最近编辑的笔记
在主页面的State类中,我们实现了笔记列表的获取与展示:
dart复制class _HomePageState extends State<HomePage> {
late Future<List<Note>> notesFuture;
@override
void initState() {
super.initState();
refreshNotes();
}
Future refreshNotes() async {
setState(() {
notesFuture = NotesDatabase.instance.readAllNotes();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('最近编辑'),
),
body: FutureBuilder<List<Note>>(
future: notesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('错误: ${snapshot.error}'));
} else if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final note = snapshot.data![index];
return _buildNoteCard(note, index);
},
);
} else {
return const Center(child: Text('暂无笔记'));
}
},
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () async {
await Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const NoteEditPage()),
);
refreshNotes();
},
),
);
}
Widget _buildNoteCard(Note note, int index) {
return Card(
margin: const EdgeInsets.all(8),
child: ListTile(
title: Text(note.title),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
note.content.length > 50
? '${note.content.substring(0, 50)}...'
: note.content,
),
const SizedBox(height: 4),
Text(
'最后编辑: ${DateFormat('yyyy-MM-dd HH:mm').format(note.lastEdited)}',
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
onTap: () async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => NoteEditPage(note: note),
),
);
refreshNotes();
},
),
);
}
}
3.3 编辑页面实现
编辑页面负责创建和修改笔记,关键点在于自动更新最后编辑时间:
dart复制class NoteEditPage extends StatefulWidget {
final Note? note;
const NoteEditPage({Key? key, this.note}) : super(key: key);
@override
_NoteEditPageState createState() => _NoteEditPageState();
}
class _NoteEditPageState extends State<NoteEditPage> {
late final TextEditingController _titleController;
late final TextEditingController _contentController;
@override
void initState() {
super.initState();
_titleController = TextEditingController(text: widget.note?.title ?? '');
_contentController = TextEditingController(text: widget.note?.content ?? '');
}
@override
void dispose() {
_titleController.dispose();
_contentController.dispose();
super.dispose();
}
Future _saveNote() async {
final title = _titleController.text;
final content = _contentController.text;
if (title.isEmpty) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('提示'),
content: const Text('标题不能为空'),
actions: [
TextButton(
child: const Text('确定'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
return;
}
if (widget.note != null) {
// 更新现有笔记
final updatedNote = widget.note!.copy(
title: title,
content: content,
);
await NotesDatabase.instance.updateNote(updatedNote);
} else {
// 创建新笔记
final newNote = Note(
title: title,
content: content,
lastEdited: DateTime.now(),
);
await NotesDatabase.instance.createNote(newNote);
}
Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.note == null ? '新建笔记' : '编辑笔记'),
actions: [
IconButton(
icon: const Icon(Icons.save),
onPressed: _saveNote,
),
],
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _titleController,
decoration: const InputDecoration(
hintText: '标题',
border: OutlineInputBorder(),
),
style: Theme.of(context).textTheme.headline6,
),
const SizedBox(height: 16),
Expanded(
child: TextField(
controller: _contentController,
decoration: const InputDecoration(
hintText: '内容',
border: InputBorder.none,
),
maxLines: null,
expands: true,
keyboardType: TextInputType.multiline,
),
),
],
),
),
);
}
}
4. OpenHarmony适配与打包发布
4.1 Flutter for OpenHarmony的特殊配置
要让Flutter应用在OpenHarmony上运行,需要进行一些特殊配置:
- 在android/app/build.gradle中确保minSdkVersion至少为8:
gradle复制defaultConfig {
minSdkVersion 8
targetSdkVersion 8
}
- 在ohos/entry/build-profile.json5中配置hap包信息:
json复制{
"app": {
"signingConfigs": [],
"compileSdkVersion": 8,
"compatibleSdkVersion": 8,
"products": [
{
"name": "default",
"signingConfig": "default",
"compileSdkVersion": 8
}
]
}
}
4.2 构建与调试
构建OpenHarmony版本的命令:
bash复制flutter build ohos
调试时可以使用以下命令启动应用:
bash复制flutter run -d ohos
实际测试中发现,OpenHarmony模拟器对Flutter的支持还在完善中,建议使用真机调试以获得最佳体验。我在华为P50 Pro上测试运行流畅,所有功能正常。
4.3 性能优化技巧
-
数据库操作优化:
- 使用批量操作减少IO次数
- 对频繁查询的字段建立索引
- 在OpenHarmony上,sqflite的性能表现略低于Android/iOS,建议控制单次查询的数据量
-
列表渲染优化:
- 使用ListView.builder的itemExtent属性固定项高度
- 对复杂卡片使用const构造函数
- 在OpenHarmony上,Flutter的列表滚动性能表现良好,但建议限制同时显示的项数
-
状态管理优化:
- 使用Provider或Riverpod等状态管理方案
- 避免不必要的全局重建
- OpenHarmony的状态更新开销略高,需要特别注意重建范围
5. 常见问题与解决方案
5.1 Flutter与OpenHarmony兼容性问题
问题1:运行时报错"MissingPluginException"
解决方案:
- 检查所有插件是否支持OpenHarmony
- 对于不支持的插件,寻找替代方案或自行实现
- 在pubspec.yaml中明确指定插件版本
问题2:UI渲染异常
解决方案:
- 确保使用Flutter官方支持的组件
- 避免使用平台特定的UI特性
- 在OpenHarmony上测试所有自定义绘制逻辑
5.2 数据库相关问题
问题1:数据库初始化失败
解决方案:
dart复制Future<Database> _initDB(String filePath) async {
try {
final dbPath = await getApplicationDocumentsDirectory();
final path = join(dbPath.path, filePath);
return await openDatabase(path, version: 1, onCreate: _createDB);
} catch (e) {
// 处理OpenHarmony上的特殊路径问题
final alternatePath = '/data/data/com.example.recentnotes/files/$filePath';
return await openDatabase(alternatePath, version: 1, onCreate: _createDB);
}
}
问题2:并发访问冲突
解决方案:
- 使用单例模式管理数据库实例
- 对写操作加锁
- 使用事务处理批量操作
5.3 打包与发布问题
问题1:签名配置错误
解决方案:
- 在ohos/entry/signingConfigs目录下配置签名文件
- 确保build-profile.json5中引用了正确的签名配置
- 使用OpenHarmony提供的签名工具生成正确的证书
问题2:hap包安装失败
解决方案:
- 检查设备是否开启"允许安装未知来源应用"
- 确认hap包版本号递增
- 清理设备上的旧版本后再安装
6. 项目扩展与进阶方向
这个基础记事本应用还可以进一步扩展:
-
云同步功能:
- 集成华为AGC云数据库
- 实现多设备同步
- 处理冲突合并
-
富文本编辑:
- 集成flutter_quill等富文本编辑器
- 支持图片插入
- 实现Markdown预览
-
智能功能:
- 基于自然语言处理的智能分类
- 时间提醒功能
- 内容搜索与标签系统
-
主题与个性化:
- 实现深色模式
- 支持自定义主题色
- 笔记封面与分类图标
在实际开发中,我发现Flutter在OpenHarmony上的表现已经相当稳定,基本功能都能完美运行。性能方面,简单的UI操作几乎与原生无异,但在复杂动画和大量数据渲染时还有优化空间。最大的优势是代码复用率——同样的Dart代码可以同时运行在Android、iOS和OpenHarmony上,大大降低了多平台开发的成本。
