1. 项目背景与目标
在移动应用开发领域,Flutter作为Google推出的跨平台框架,以其高效的渲染性能和丰富的组件库赢得了广泛关注。而OpenHarmony作为国产开源操作系统,正在构建自己的生态体系。将Flutter应用于OpenHarmony平台,不仅能够复用Flutter的跨平台优势,还能为OpenHarmony生态带来更多应用可能性。
本项目旨在开发一个轻量级的开源记事本应用,核心功能聚焦于"最近编辑"这一实用特性。通过这个实战项目,我们将探索:
- Flutter在OpenHarmony平台的适配情况
- 基础数据存储方案的实现
- 最近编辑记录的高效管理
- 应用状态保持与恢复机制
这个项目特别适合想要了解Flutter跨平台开发,同时又对OpenHarmony生态感兴趣的开发者。我们将从环境搭建开始,逐步实现一个完整可用的应用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境准备
2.1 Flutter SDK安装与配置
首先需要安装Flutter SDK并配置开发环境:
bash复制# 下载Flutter SDK
git clone https://github.com/flutter/flutter.git -b stable
# 添加环境变量
export PATH="$PATH:`pwd`/flutter/bin"
# 运行doctor检查环境
flutter doctor
对于OpenHarmony开发,还需要特别注意:
- 确保Flutter版本在3.0以上,以获得更好的鸿蒙支持
- 安装OpenHarmony开发工具链(DevEco Studio)
- 配置OpenHarmony设备或模拟器
提示:在MacOS上可能会遇到权限问题,可以通过
chmod -R 755 flutter命令解决。
2.2 OpenHarmony模拟器搭建
推荐使用QEMU模拟器进行开发测试,以下是快速搭建步骤:
- 下载OpenHarmony 6.1镜像
- 安装QEMU虚拟化环境
- 配置模拟器参数:
bash复制qemu-system-aarch64 -m 2048 -cpu cortex-a57 -smp 4 \ -kernel zImage -initrd rootfs.img \ -append "root=/dev/ram0 console=ttyAMA0" \ -net nic -net user,hostfwd=tcp::5022-:22 \ -nographic - 启动模拟器并验证连接
2.3 项目初始化
创建Flutter项目并添加OpenHarmony支持:
bash复制flutter create oh_note
cd oh_note
flutter pub add shared_preferences path_provider
在pubspec.yaml中添加OpenHarmony平台支持:
yaml复制flutter:
module:
androidX: true
platforms:
openharmony:
package: com.example.oh_note
3. 应用架构设计
3.1 整体架构
我们采用经典的MVVM架构模式:
code复制┌───────────────────────────────────────┐
│ UI Layer │
│ ┌─────────────┐ ┌─────────────┐│
│ │ Widgets │<---->│ ViewModel ││
│ └─────────────┘ └─────────────┘│
└───────────────────┬───────────────────┘
│
┌───────────────────▼───────────────────┐
│ Data Layer │
│ ┌─────────────┐ ┌─────────────┐│
│ │ Repository │<---->│ Local Store ││
│ └─────────────┘ └─────────────┘│
└───────────────────────────────────────┘
3.2 核心数据结构
定义笔记数据模型:
dart复制class Note {
final String id;
String title;
String content;
DateTime createdAt;
DateTime updatedAt;
Note({
required this.id,
required this.title,
required this.content,
DateTime? createdAt,
DateTime? updatedAt,
}) :
createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'content': content,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
};
factory Note.fromJson(Map<String, dynamic> json) => Note(
id: json['id'],
title: json['title'],
content: json['content'],
createdAt: DateTime.parse(json['createdAt']),
updatedAt: DateTime.parse(json['updatedAt']),
);
}
3.3 状态管理方案
我们使用provider进行状态管理,安装依赖:
bash复制flutter pub add provider
创建全局状态管理类:
dart复制class NoteProvider with ChangeNotifier {
final List<Note> _notes = [];
final List<String> _recentlyEdited = [];
List<Note> get notes => _notes;
List<String> get recentlyEdited => _recentlyEdited;
void addNote(Note note) {
_notes.add(note);
_updateRecentlyEdited(note.id);
notifyListeners();
}
void _updateRecentlyEdited(String noteId) {
_recentlyEdited.remove(noteId);
_recentlyEdited.insert(0, noteId);
if (_recentlyEdited.length > 5) {
_recentlyEdited.removeLast();
}
}
}
4. 核心功能实现
4.1 数据持久化存储
我们使用shared_preferences和文件系统结合的方式存储数据:
- 创建存储服务类:
dart复制class StorageService {
static const String _notesKey = 'notes';
static const String _recentKey = 'recent_notes';
final SharedPreferences _prefs;
final Directory _documentsDir;
StorageService(this._prefs, this._documentsDir);
Future<void> saveNotes(List<Note> notes) async {
final notesJson = notes.map((note) => note.toJson()).toList();
await _prefs.setString(_notesKey, jsonEncode(notesJson));
}
Future<List<Note>> loadNotes() async {
final notesJson = _prefs.getString(_notesKey);
if (notesJson == null) return [];
final List<dynamic> jsonList = jsonDecode(notesJson);
return jsonList.map((json) => Note.fromJson(json)).toList();
}
Future<File> _getNoteFile(String noteId) async {
return File('${_documentsDir.path}/$noteId.txt');
}
Future<void> saveNoteContent(String noteId, String content) async {
final file = await _getNoteFile(noteId);
await file.writeAsString(content);
}
Future<String> loadNoteContent(String noteId) async {
try {
final file = await _getNoteFile(noteId);
return await file.readAsString();
} catch (e) {
return '';
}
}
}
- 初始化存储服务:
dart复制Future<StorageService> initStorage() async {
final prefs = await SharedPreferences.getInstance();
final dir = await getApplicationDocumentsDirectory();
return StorageService(prefs, dir);
}
4.2 最近编辑功能实现
"最近编辑"功能的核心在于维护一个按编辑时间排序的笔记ID列表:
- 扩展存储服务:
dart复制Future<void> updateRecentlyEdited(List<String> noteIds) async {
await _prefs.setStringList(_recentKey, noteIds);
}
Future<List<String>> loadRecentlyEdited() async {
return _prefs.getStringList(_recentKey) ?? [];
}
- 在ViewModel中集成:
dart复制class NoteViewModel with ChangeNotifier {
final StorageService storage;
List<Note> notes = [];
List<String> recentlyEdited = [];
NoteViewModel(this.storage);
Future<void> loadData() async {
notes = await storage.loadNotes();
recentlyEdited = await storage.loadRecentlyEdited();
notifyListeners();
}
Future<void> saveNote(Note note, String content) async {
// 更新笔记内容
await storage.saveNoteContent(note.id, content);
// 更新最近编辑列表
if (!recentlyEdited.contains(note.id)) {
recentlyEdited.insert(0, note.id);
} else {
recentlyEdited.remove(note.id);
recentlyEdited.insert(0, note.id);
}
// 保持最多5个最近编辑
if (recentlyEdited.length > 5) {
recentlyEdited = recentlyEdited.sublist(0, 5);
}
await storage.updateRecentlyEdited(recentlyEdited);
notifyListeners();
}
}
4.3 UI界面构建
- 主页面布局:
dart复制class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('OH Note'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () => _createNewNote(context),
),
],
),
body: Consumer<NoteViewModel>(
builder: (context, model, child) {
return Column(
children: [
_buildRecentlyEditedSection(model),
Expanded(
child: _buildAllNotesList(model),
),
],
);
},
),
);
}
Widget _buildRecentlyEditedSection(NoteViewModel model) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16),
child: Text(
'最近编辑',
style: Theme.of(context).textTheme.headline6,
),
),
SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: model.recentlyEdited.length,
itemBuilder: (context, index) {
final noteId = model.recentlyEdited[index];
final note = model.notes.firstWhere(
(n) => n.id == noteId,
orElse: () => Note(
id: '',
title: '已删除',
content: '',
),
);
return _buildNoteCard(note);
},
),
),
],
);
}
}
- 笔记编辑页面:
dart复制class NoteEditPage extends StatefulWidget {
final Note note;
NoteEditPage({required this.note});
@override
_NoteEditPageState createState() => _NoteEditPageState();
}
class _NoteEditPageState extends State<NoteEditPage> {
late TextEditingController _titleController;
late TextEditingController _contentController;
@override
void initState() {
super.initState();
_titleController = TextEditingController(text: widget.note.title);
_contentController = TextEditingController();
_loadContent();
}
Future<void> _loadContent() async {
final model = Provider.of<NoteViewModel>(context, listen: false);
final content = await model.storage.loadNoteContent(widget.note.id);
_contentController.text = content;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
controller: _titleController,
decoration: InputDecoration(
hintText: '输入标题',
border: InputBorder.none,
),
style: Theme.of(context).textTheme.headline6,
),
actions: [
IconButton(
icon: Icon(Icons.save),
onPressed: _saveNote,
),
],
),
body: Padding(
padding: EdgeInsets.all(16),
child: TextField(
controller: _contentController,
decoration: InputDecoration(
hintText: '开始输入...',
border: InputBorder.none,
),
maxLines: null,
keyboardType: TextInputType.multiline,
),
),
);
}
Future<void> _saveNote() async {
final model = Provider.of<NoteViewModel>(context, listen: false);
await model.saveNote(
widget.note.copyWith(title: _titleController.text),
_contentController.text,
);
Navigator.pop(context);
}
}
5. OpenHarmony平台适配
5.1 平台特定配置
在oh-package.json5中添加OpenHarmony配置:
json复制{
"app": {
"bundleName": "com.example.oh_note",
"vendor": "example",
"versionCode": 1,
"versionName": "1.0",
"icon": "$media:app_icon",
"label": "$string:app_name"
}
}
5.2 权限声明
在config.json中添加必要的权限:
json复制{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.READ_USER_STORAGE",
"reason": "读取笔记内容"
},
{
"name": "ohos.permission.WRITE_USER_STORAGE",
"reason": "保存笔记内容"
}
]
}
}
5.3 鸿蒙特性集成
利用OpenHarmony的分布式能力增强应用:
dart复制import 'package:flutter/services.dart';
class OhosUtils {
static const MethodChannel _channel = MethodChannel('com.example/ohos');
static Future<bool> isDistributedReady() async {
try {
return await _channel.invokeMethod('isDistributedReady');
} on PlatformException {
return false;
}
}
static Future<void> syncNoteToOtherDevices(Note note) async {
try {
await _channel.invokeMethod('syncNote', note.toJson());
} on PlatformException catch (e) {
debugPrint('同步失败: ${e.message}');
}
}
}
6. 测试与优化
6.1 单元测试
为ViewModel编写测试用例:
dart复制void main() {
late MockStorageService mockStorage;
late NoteViewModel viewModel;
setUp(() {
mockStorage = MockStorageService();
viewModel = NoteViewModel(mockStorage);
});
test('加载数据后更新状态', () async {
when(mockStorage.loadNotes()).thenAnswer((_) async => [
Note(id: '1', title: '测试', content: '内容'),
]);
when(mockStorage.loadRecentlyEdited()).thenAnswer((_) async => ['1']);
await viewModel.loadData();
expect(viewModel.notes.length, 1);
expect(viewModel.recentlyEdited.length, 1);
});
test('保存笔记更新最近编辑列表', () async {
final note = Note(id: '1', title: '测试', content: '内容');
when(mockStorage.saveNoteContent(any, any)).thenAnswer((_) async {});
when(mockStorage.updateRecentlyEdited(any)).thenAnswer((_) async {});
await viewModel.saveNote(note, '新内容');
verify(mockStorage.updateRecentlyEdited(['1'])).called(1);
});
}
6.2 性能优化
- 使用
ListView.builder的itemExtent提高滚动性能 - 实现笔记内容的懒加载
- 添加防抖机制保存笔记:
dart复制Timer? _saveDebounce;
Future<void> saveNoteWithDebounce(Note note, String content) async {
_saveDebounce?.cancel();
_saveDebounce = Timer(const Duration(milliseconds: 500), () {
saveNote(note, content);
});
}
6.3 内存管理
- 在页面销毁时释放控制器:
dart复制@override
void dispose() {
_titleController.dispose();
_contentController.dispose();
super.dispose();
}
- 使用
AutomaticKeepAliveClientMixin保持页面状态:
dart复制class NoteEditPage extends StatefulWidget {
// ...
}
class _NoteEditPageState extends State<NoteEditPage>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
// ...
}
7. 项目构建与发布
7.1 构建OpenHarmony应用包
- 配置构建脚本:
bash复制flutter build openharmony --release
- 生成HAP包:
bash复制cd build/openharmony
hvigor assembleRelease
7.2 应用签名
- 生成签名证书:
bash复制keytool -genkeypair -alias "oh_note" -keyalg RSA -keysize 2048 \
-validity 365 -keystore oh_note.jks
- 配置签名信息:
在build.gradle中添加:
groovy复制android {
signingConfigs {
release {
storeFile file('oh_note.jks')
storePassword 'yourpassword'
keyAlias 'oh_note'
keyPassword 'yourpassword'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
7.3 发布到应用市场
-
准备应用元数据:
- 应用图标(多种分辨率)
- 截图展示
- 应用描述
- 隐私政策链接
-
提交到OpenHarmony应用市场:
- 登录开发者中心
- 创建新应用
- 上传HAP包
- 填写应用信息
- 提交审核
8. 项目扩展方向
这个基础记事本应用还有很大的扩展空间:
- 云同步功能:集成华为云或其它云存储服务
- Markdown支持:添加富文本编辑能力
- 标签系统:为笔记添加分类标签
- 搜索功能:实现全文搜索
- 回收站机制:防止误删除
- 夜间模式:支持主题切换
- 桌面小工具:快速查看最近编辑的笔记
在实现这些扩展功能时,需要注意保持应用的轻量级特性,避免功能膨胀。可以根据实际需求选择最有价值的功能进行扩展。
