1. 项目背景与目标解析
Flutter作为Google推出的跨平台开发框架,近年来在移动应用开发领域获得了广泛关注。而OpenHarmony作为新兴的分布式操作系统,其生态建设正处于快速发展阶段。将Flutter应用于OpenHarmony平台开发,能够充分发挥"一次编写,多端运行"的优势,同时为OpenHarmony生态带来更多高质量应用。
这个轻量级开源记事本APP项目,旨在为开发者提供一个完整的实战案例,展示如何利用Flutter框架开发适配OpenHarmony系统的实用工具类应用。记事本作为基础工具应用,涵盖了数据存储、UI交互、状态管理等核心开发场景,是学习跨平台开发的理想切入点。
提示:选择记事本作为练手项目,不仅因为其功能相对简单,更因为它能完整覆盖移动应用开发的典型场景,包括CRUD操作、本地持久化、列表展示等核心功能。
2. 开发环境准备与配置
2.1 Flutter环境搭建
首先需要安装Flutter SDK,建议使用最新稳定版本(目前为3.19.x)。安装完成后,通过以下命令验证环境:
bash复制flutter doctor
这个命令会检查所有必需的开发工具是否已正确安装和配置,包括:
- Flutter SDK路径
- Android工具链(虽然开发OpenHarmony应用,但仍需要部分Android工具)
- IDE插件(VS Code或Android Studio)
- 网络连接
对于OpenHarmony开发,还需要额外配置鸿蒙开发环境:
- 下载并安装DevEco Studio(鸿蒙官方IDE)
- 安装OpenHarmony SDK
- 配置鸿蒙设备连接(真机或模拟器)
2.2 项目创建与初始化
使用以下命令创建新项目:
bash复制flutter create oh_note_app
cd oh_note_app
创建完成后,需要修改pubspec.yaml文件,添加OpenHarmony平台支持:
yaml复制flutter:
uses-material-design: true
generate: true
platforms:
ohos:
package: com.example.oh_note_app
3. 项目架构设计
3.1 目录结构规划
良好的项目结构是维护性和扩展性的基础。建议采用以下目录结构:
code复制lib/
├── models/ # 数据模型
├── services/ # 业务逻辑服务
├── repositories/ # 数据存储层
├── pages/ # 页面组件
├── widgets/ # 可复用UI组件
├── utils/ # 工具类
└── main.dart # 应用入口
3.2 状态管理方案选择
对于小型记事本应用,推荐使用provider作为状态管理方案,它足够轻量且易于理解。在pubspec.yaml中添加依赖:
yaml复制dependencies:
provider: ^6.0.5
4. 核心功能实现
4.1 数据模型定义
首先定义记事本笔记的数据模型:
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用于存储
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'content': content,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
};
}
// 从Map创建Note对象
factory Note.fromMap(Map<String, dynamic> map) {
return Note(
id: map['id'],
title: map['title'],
content: map['content'],
createdAt: DateTime.parse(map['createdAt']),
updatedAt: DateTime.parse(map['updatedAt']),
);
}
}
4.2 本地存储实现
对于轻量级记事本应用,使用shared_preferences即可满足需求。首先添加依赖:
yaml复制dependencies:
shared_preferences: ^2.2.2
然后实现存储服务:
dart复制class NoteRepository {
static const _notesKey = 'notes';
final SharedPreferences _prefs;
NoteRepository(this._prefs);
Future<List<Note>> getAllNotes() async {
final notesJson = _prefs.getStringList(_notesKey) ?? [];
return notesJson.map((json) => Note.fromMap(jsonDecode(json))).toList();
}
Future<void> saveNote(Note note) async {
final notes = await getAllNotes();
final existingIndex = notes.indexWhere((n) => n.id == note.id);
if (existingIndex >= 0) {
notes[existingIndex] = note;
} else {
notes.add(note);
}
await _prefs.setStringList(
_notesKey,
notes.map((n) => jsonEncode(n.toMap())).toList(),
);
}
Future<void> deleteNote(String id) async {
final notes = await getAllNotes();
notes.removeWhere((n) => n.id == id);
await _prefs.setStringList(
_notesKey,
notes.map((n) => jsonEncode(n.toMap())).toList(),
);
}
}
4.3 UI界面开发
4.3.1 笔记列表页面
dart复制class NotesListPage extends StatelessWidget {
const NotesListPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('我的记事本'),
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
// 实现搜索功能
},
),
],
),
body: Consumer<NotesProvider>(
builder: (context, provider, child) {
if (provider.notes.isEmpty) {
return const Center(
child: Text('暂无笔记,点击右下角按钮添加'),
);
}
return ListView.builder(
itemCount: provider.notes.length,
itemBuilder: (context, index) {
final note = provider.notes[index];
return ListTile(
title: Text(note.title),
subtitle: Text(
note.content.length > 50
? '${note.content.substring(0, 50)}...'
: note.content,
),
trailing: Text(
DateFormat('yyyy-MM-dd').format(note.updatedAt),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NoteEditPage(note: note),
),
);
},
);
},
);
},
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NoteEditPage(),
),
);
},
),
);
}
}
4.3.2 笔记编辑页面
dart复制class NoteEditPage extends StatefulWidget {
final Note? note;
const NoteEditPage({super.key, this.note});
@override
State<NoteEditPage> 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();
}
@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.0),
child: Column(
children: [
TextField(
controller: _titleController,
decoration: const InputDecoration(
labelText: '标题',
border: OutlineInputBorder(),
),
style: Theme.of(context).textTheme.headline6,
),
const SizedBox(height: 16),
Expanded(
child: TextField(
controller: _contentController,
decoration: const InputDecoration(
labelText: '内容',
border: OutlineInputBorder(),
alignLabelWithHint: true,
),
maxLines: null,
expands: true,
keyboardType: TextInputType.multiline,
),
),
],
),
),
);
}
Future<void> _saveNote() async {
final title = _titleController.text.trim();
final content = _contentController.text.trim();
if (title.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('标题不能为空')),
);
return;
}
final note = widget.note != null
? widget.note!.copyWith(
title: title,
content: content,
updatedAt: DateTime.now(),
)
: Note(
id: const Uuid().v4(),
title: title,
content: content,
);
await Provider.of<NotesProvider>(context, listen: false).saveNote(note);
Navigator.pop(context);
}
}
5. OpenHarmony适配与优化
5.1 鸿蒙特性适配
为了使应用更好地融入OpenHarmony生态系统,需要进行一些特定适配:
- 应用图标和名称适配:在
ohos/resources目录下配置符合鸿蒙规范的应用图标和名称 - 权限申请:在
config.json中声明应用所需的权限 - 鸿蒙UI风格适配:调整Material Design组件的外观,使其更符合鸿蒙的设计语言
5.2 性能优化
- 列表性能优化:对于大量笔记的情况,使用
ListView.builder的itemExtent属性固定项高度 - 图片加载优化:如果支持图片笔记,使用
cached_network_image等库优化图片加载 - 数据库优化:当笔记数量较多时,考虑使用SQLite替代shared_preferences
6. 测试与调试
6.1 单元测试
为关键业务逻辑编写单元测试:
dart复制void main() {
group('NoteRepository', () {
late SharedPreferences prefs;
late NoteRepository repository;
setUp(() async {
SharedPreferences.setMockInitialValues({});
prefs = await SharedPreferences.getInstance();
repository = NoteRepository(prefs);
});
test('save and retrieve note', () async {
final note = Note(
id: '1',
title: 'Test Note',
content: 'This is a test note',
);
await repository.saveNote(note);
final notes = await repository.getAllNotes();
expect(notes.length, 1);
expect(notes[0].title, 'Test Note');
});
test('delete note', () async {
final note = Note(
id: '1',
title: 'Test Note',
content: 'This is a test note',
);
await repository.saveNote(note);
await repository.deleteNote('1');
final notes = await repository.getAllNotes();
expect(notes.length, 0);
});
});
}
6.2 集成测试
编写集成测试验证整个应用流程:
dart复制void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('create and edit note', (tester) async {
// 启动应用
await tester.pumpWidget(const MyApp());
// 点击添加按钮
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
// 输入标题和内容
await tester.enterText(find.byType(TextField).first, 'Test Note');
await tester.enterText(find.byType(TextField).last, 'This is a test');
// 点击保存
await tester.tap(find.byIcon(Icons.save));
await tester.pumpAndSettle();
// 验证笔记已添加
expect(find.text('Test Note'), findsOneWidget);
// 点击笔记进入编辑
await tester.tap(find.text('Test Note'));
await tester.pumpAndSettle();
// 修改内容
await tester.enterText(find.byType(TextField).last, 'Updated content');
// 点击保存
await tester.tap(find.byIcon(Icons.save));
await tester.pumpAndSettle();
// 验证内容已更新
expect(find.text('Updated content'), findsOneWidget);
});
}
7. 打包与发布
7.1 构建OpenHarmony应用包
使用以下命令构建应用:
bash复制flutter build ohos
构建完成后,可以在build/ohos/app/release目录下找到.hap安装包文件。
7.2 发布到鸿蒙应用市场
- 注册成为鸿蒙开发者
- 准备应用元数据(描述、截图等)
- 提交应用审核
- 审核通过后发布
8. 项目扩展与进阶
完成基础记事本功能后,可以考虑以下扩展方向:
- 云同步功能:集成华为云或其他云服务,实现多设备同步
- 富文本编辑:支持粗体、斜体、列表等富文本格式
- 分类与标签:为笔记添加分类和标签,方便管理
- 回收站功能:删除的笔记先进入回收站,避免误删
- 夜间模式:支持暗黑主题,保护夜间使用者的眼睛
注意:在添加新功能时,要保持代码的模块化和可维护性,每个新功能应该独立封装,避免过度耦合。
