Flutter-OH 鸿蒙工具类应用实战 Day9:笔记分类标签与多条件筛选功能实现
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net。
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
一、使用软件
DevEco Studio
鸿蒙官方集成开发环境,提供项目管理、代码编辑、模拟器调试、应用打包等全流程开发能力,是 Flutter-OH 应用开发的核心工具。

Flutter-OH SDK
面向 OpenHarmony 生态定制的 Flutter 跨平台开发工具包,支持 Dart 语言编译、鸿蒙平台适配、UI 组件渲染与应用构建
OpenHarmony 模拟器
DevEco Studio 内置的鸿蒙设备虚拟运行环境,无需物理真机即可完成 APP 界面预览、功能调试与兼容性验证。
二、核心内容
- 扩展笔记实体类,新增 tag 分类标签,分为学习、工作、日常三类。
- 编辑页面添加下拉选择框,新建笔记可以手动选择所属分类。
- 首页顶部添加分类导航按钮栏,支持全部、学习、工作、日常筛选笔记。
- 使用原生本地存储,保存笔记分类信息,重启模拟器数据不丢失。
- 修复之前分类不显示、筛选没反应的问题,完善分类逻辑。
- 保留搜索、新增、卡片布局、笔记查看等原有全部功能。
三、操作步骤(附带完整代码)
步骤 1:配置 pubspec.yaml 基础依赖
删除无效鸿蒙第三方库,只保留基础依赖,避免报错。
name: note_app
description: 鸿蒙记事本
publish_to: none
version: 1.0.0+1
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.2.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
配置完成后在终端执行:
flutter pub get
步骤 2:修改笔记模型类 lib/models/note_model.dart
新增分类标签字段,完善序列化保存分类数据。
class NoteModel {
String content;
String createTime;
String updateTime;
bool isStar;
String tag;
NoteModel({
required this.content,
required this.createTime,
required this.updateTime,
this.isStar = false,
this.tag = "日常",
});
Map<String,dynamic> toJson(){
return {
"content":content,
"createTime":createTime,
"updateTime":updateTime,
"isStar":isStar,
"tag":tag,
};
}
static NoteModel fromJson(Map<String,dynamic> map){
return NoteModel(
content: map["content"],
createTime: map["createTime"],
updateTime: map["updateTime"],
isStar: map["isStar"] ?? false,
tag: map["tag"] ?? "日常",
);
}
}
步骤 3:新建存储工具类 lib/utils/note_storage.dart
使用原生本地存储,保存笔记和分类信息
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/note_model.dart';
class NoteStorage {
static const String _key = 'note_list';
static Future<void> saveNotes(List<NoteModel> notes) async {
final prefs = await SharedPreferences.getInstance();
List<String> jsonList = notes.map((e) => jsonEncode(e.toJson())).toList();
await prefs.setStringList(_key, jsonList);
}
static Future<List<NoteModel>> loadNotes() async {
final prefs = await SharedPreferences.getInstance();
List<String>? strList = prefs.getStringList(_key);
if (strList == null) return [];
return strList.map((e) => NoteModel.fromJson(jsonDecode(e))).toList();
}
}
步骤 4:编辑页面添加下拉分类选择 lib/pages/markdown_edit_page.dart
加入下拉框,可选择学习、工作、日常,并把分类传回首页。
dart
import 'package:flutter/material.dart';
class MarkdownEditPage extends StatefulWidget {
final String? initialContent;
final String? initialTag;
const MarkdownEditPage({
super.key,
this.initialContent,
this.initialTag,
});
@override
State<MarkdownEditPage> createState() => _MarkdownEditPageState();
}
class _MarkdownEditPageState extends State<MarkdownEditPage> {
late TextEditingController _controller;
String _selectTag = "日常";
@override
void initState() {
super.initState();
_controller = TextEditingController(text: widget.initialContent ?? "");
_selectTag = widget.initialTag ?? "日常";
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("编辑笔记"),
actions: [
TextButton(
onPressed: (){
Navigator.pop(context,{
"content":_controller.text,
"tag":_selectTag
});
},
child: const Text("保存",style:TextStyle(color:Colors.white)),
)
],
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: DropdownButton<String>(
value: _selectTag,
isExpanded: true,
underline: const SizedBox(),
items: const [
DropdownMenuItem(value: "学习", child: Text("学习")),
DropdownMenuItem(value: "工作", child: Text("工作")),
DropdownMenuItem(value: "日常", child: Text("日常")),
],
onChanged: (val){
setState(() {
_selectTag = val!;
});
},
),
),
const SizedBox(height: 20),
Expanded(
child: TextField(
controller: _controller,
maxLines: null,
expands: true,
decoration: const InputDecoration(
hintText: "请输入笔记内容",
border: OutlineInputBorder(),
),
),
),
],
),
),
);
}
}
步骤 5:首页主页面 lib/main.dart(已修复分类筛选、顶部按钮、卡片标签)
dart
import 'package:flutter/material.dart';
import 'utils/note_storage.dart';
import 'pages/markdown_edit_page.dart';
import 'models/note_model.dart';
void main() {
runApp(const NoteApp());
}
class NoteApp extends StatelessWidget {
const NoteApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Note App',
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<NoteModel> _allNotes = [];
List<NoteModel> _showNotes = [];
final TextEditingController _searchController = TextEditingController();
@override
void initState() {
super.initState();
_loadNotes();
_searchController.addListener(_searchNotes);
}
Future<void> _loadNotes() async {
final list = await NoteStorage.loadNotes();
setState(() {
_allNotes = list;
_showNotes = List.from(_allNotes);
});
}
Future<void> _saveNotes() async {
await NoteStorage.saveNotes(_allNotes);
}
void _searchNotes() {
String key = _searchController.text.trim();
setState(() {
if (key.isEmpty) {
_showNotes = List.from(_allNotes);
} else {
_showNotes = _allNotes.where((note) => note.content.contains(key)).toList();
}
});
}
// 分类筛选核心方法
void _filterTag(String tag){
setState(() {
if(tag == "全部"){
_showNotes = List.from(_allNotes);
}else{
_showNotes = _allNotes.where((item) => item.tag == tag).toList();
}
});
}
// 分类对应颜色
Color _getTagColor(String tag){
if(tag == "学习") return Colors.blue;
if(tag == "工作") return Colors.green;
return Colors.grey;
}
Future<void> _addNote() async {
final res = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const MarkdownEditPage()),
);
if (res != null && res["content"].toString().trim().isNotEmpty) {
String time = DateTime.now().toString().substring(0,16);
setState(() {
_allNotes.add(
NoteModel(
content: res["content"],
tag: res["tag"],
createTime: time,
updateTime: time,
),
);
_showNotes = List.from(_allNotes);
});
await _saveNotes();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("鸿蒙记事本"),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(90),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: TextField(
controller: _searchController,
decoration: const InputDecoration(
hintText: "搜索笔记",
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
),
),
SizedBox(
height: 40,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(onPressed: ()=>_filterTag("全部"), child: const Text("全部")),
TextButton(onPressed: ()=>_filterTag("学习"), child: const Text("学习")),
TextButton(onPressed: ()=>_filterTag("工作"), child: const Text("工作")),
TextButton(onPressed: ()=>_filterTag("日常"), child: const Text("日常")),
],
),
)
],
),
),
),
body: _showNotes.isEmpty
? const Center(child: Text("暂无笔记"))
: ListView.builder(
itemCount: _showNotes.length,
itemBuilder: (context,index){
return Card(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: _getTagColor(_showNotes[index].tag),
borderRadius: BorderRadius.circular(4),
),
child: Text(
_showNotes[index].tag,
style: const TextStyle(color: Colors.white, fontSize: 10),
),
),
const SizedBox(height: 8),
Text(
_showNotes[index].content.length > 20
? "${_showNotes[index].content.substring(0,20)}..."
: _showNotes[index].content,
),
],
),
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _addNote,
child: const Icon(Icons.add),
),
);
}
}
四、800 字实训心得
本次实训为 Flutter 鸿蒙记事本开发第九天,主要完成笔记分类标签与分类筛选功能开发。为了避免第三方库地址失效、依赖拉取报错等问题,本次放弃使用远程第三方库,只采用 Flutter 原生基础依赖进行开发,项目配置简单、运行稳定,不会出现依赖找不到文件的错误。
首先修改笔记实体模型,新增 tag 分类标签字段,设定学习、工作、日常三种常用分类,并完善 JSON 序列化和反序列化代码,让每条笔记都能独立保存所属分类。随后在笔记编辑页面添加下拉选择控件,新建笔记时可以自由选择分类类型,选择后将分类数据传回首页进行保存。
在首页顶部新增分类导航按钮栏,设置全部、学习、工作、日常四个选项,编写分类筛选逻辑方法,点击对应按钮即可动态过滤笔记列表,只展示对应分类内容,实现笔记快速分类查找。直观区分笔记类型,界面更加整洁美观。
开发过程中遇到过分类不显示、筛选无反应的问题,主要是列表没有及时刷新、分类赋值逻辑不完善导致。通过重新优化筛选方法、刷新列表数据,成功修复分类功能 BUG,实现正常切换筛选。项目沿用原生本地存储方式,能够稳定保存笔记内容和分类标签,重启模拟器后数据不会丢失,同时保留搜索、新增笔记、卡片布局等原有全部功能,互不冲突。
通过本次第九天实训,我掌握了数据模型扩展、下拉选择组件使用、条件筛选逻辑编写、原生本地存储的使用方法,同时学会排查功能失效 BUG,进一步熟悉了 Flutter 页面布局与交互逻辑,为后续项目打包优化打下良好基础。
五、模拟器运行测试
- 新建笔记可正常下拉选择学习、工作、日常分类。
- 顶部分类按钮可正常切换,精准筛选对应笔记。
- 重启模拟器,笔记和分类数据完整保留不丢失。
- 搜索功能、卡片布局、新增笔记全部正常兼容。


更多推荐

所有评论(0)