【开源鸿蒙跨平台开发先锋训练营】基于Flutter实现鸿蒙App收藏/喜欢功能(含本地持久化方案)
本文介绍了在鸿蒙App开发中实现本地数据持久化的收藏功能方案。通过shared_preferences插件存储两份数据:作品ID集合用于快速判断收藏状态,作品列表用于收藏页展示。核心实现包括:1)创建单例收藏服务类,封装ID和作品的增删查改操作;2)在发现页、详情页和收藏页中维护收藏状态同步;3)采用懒加载方式初始化SharedPreferences。该方案避免了重复接口请求,保证了各页面收藏状态
·
这里带大家一起交流学习下在鸿蒙App开发中如何对数据进行本地持久化。
一、功能说明
- 入口:发现页卡片心形、作品详情页顶部收藏按钮;「我的」→「我的收藏」。
- 行为:点击心形/收藏按钮可收藏或取消收藏;我的收藏页展示已收藏作品列表,点击进入详情;从详情返回后列表自动刷新。
- 数据:使用
shared_preferences持久化,存两份:作品 ID 集合(用于快速判断是否已收藏)、作品快照列表(用于我的收藏页展示,无需再请求接口)。
二、涉及文件
| 文件 | 说明 |
|---|---|
pubspec.yaml |
增加 shared_preferences 依赖 |
lib/services/favorites_service.dart |
收藏服务(单例):存/取/删 ID 与作品快照 |
lib/pages/search_page.dart |
发现页:收藏状态、心形点击、从详情返回后刷新 |
lib/pages/work_detail_page.dart |
作品详情:顶部收藏按钮、切换状态 |
lib/pages/favorites_page.dart |
我的收藏页:列表、空态、下拉刷新、进入详情后刷新 |
lib/pages/profile_page.dart |
我的页:增加「我的收藏」入口与跳转 |
三、依赖
在 pubspec.yaml 的 dependencies 下增加:
# 本地持久化(收藏、主题、搜索历史等)
shared_preferences: ^2.2.2
执行 flutter pub get 安装。
四、收藏服务(核心逻辑)
文件:lib/services/favorites_service.dart
- 单例,键:
favorites_ids(ID 列表 JSON)、favorites_works(作品toJson列表 JSON)。 - 懒加载
SharedPreferences,避免重复getInstance()。在这里插入图片描述

关键代码:
/// 收藏服务:本地持久化作品 ID 列表与作品快照,供「我的收藏」列表展示
class FavoritesService {
static const String _keyIds = 'favorites_ids';
static const String _keyWorks = 'favorites_works';
static final FavoritesService _instance = FavoritesService._();
factory FavoritesService() => _instance;
FavoritesService._();
SharedPreferences? _prefs;
Future<SharedPreferences> get _store async {
_prefs ??= await SharedPreferences.getInstance();
return _prefs!;
}
/// 是否已收藏
Future<bool> isFavorite(String workId) async {
final ids = await getFavoriteIds();
return ids.contains(workId);
}
/// 收藏 ID 列表(用于发现页/详情页判断红心状态)
Future<Set<String>> getFavoriteIds() async {
final prefs = await _store;
final json = prefs.getString(_keyIds);
if (json == null || json.isEmpty) return {};
try {
final list = jsonDecode(json) as List<dynamic>?;
return (list ?? []).map((e) => e.toString()).toSet();
} catch (_) {
return {};
}
}
/// 收藏作品列表(用于「我的收藏」页,直接展示,无需接口)
Future<List<WorkModel>> getFavoriteWorks() async {
final prefs = await _store;
final json = prefs.getString(_keyWorks);
if (json == null || json.isEmpty) return [];
try {
final list = jsonDecode(json) as List<dynamic>?;
if (list == null) return [];
return list
.map((e) => WorkModel.fromJson(Map<String, dynamic>.from(e as Map)))
.toList();
} catch (_) {
return [];
}
}
/// 添加收藏:同时维护 ID 集合与作品快照列表(新收藏插到列表头)
Future<void> addFavorite(WorkModel work) async {
final prefs = await _store;
final ids = await getFavoriteIds();
if (ids.contains(work.id)) return;
ids.add(work.id);
final works = await getFavoriteWorks();
works.removeWhere((w) => w.id == work.id);
works.insert(0, work);
await prefs.setString(_keyIds, jsonEncode(ids.toList()));
await prefs.setString(
_keyWorks, jsonEncode(works.map((e) => e.toJson()).toList()));
}
/// 取消收藏
Future<void> removeFavorite(String workId) async {
final prefs = await _store;
final ids = await getFavoriteIds();
ids.remove(workId);
final works = await getFavoriteWorks();
works.removeWhere((w) => w.id == workId);
await prefs.setString(_keyIds, jsonEncode(ids.toList()));
await prefs.setString(
_keyWorks, jsonEncode(works.map((e) => e.toJson()).toList()));
}
/// 切换收藏状态(发现页/详情页点击心形时调用)
Future<bool> toggleFavorite(WorkModel work) async {
final ids = await getFavoriteIds();
if (ids.contains(work.id)) {
await removeFavorite(work.id);
return false;
} else {
await addFavorite(work);
return true;
}
}
}
说明:
- 用
work.id去重;列表按「最近收藏在前」顺序保存。 - 写入时同时更新
_keyIds与_keyWorks,保证「是否收藏」与「我的收藏列表」一致。
五、发现页(收藏状态 + 心形点击)
文件:lib/pages/search_page.dart
- 状态:
Set<String> _favoriteIds、FavoritesService _favoritesService。 - 进入页面:
initState里_loadWorks()后调用_refreshFavoriteIds();从详情返回时_openWorkDetail的Navigator.push(...).then((_) => _refreshFavoriteIds()),保证列表红心与详情内操作一致。 - 卡片:
_WorkCard增加isFavorite、onFavoriteTap;心形用IconButton,已收藏显示红心、未收藏显示灰心,点击调用_toggleFavorite(work)再_refreshFavoriteIds()。

关键代码片段:
// 状态
Set<String> _favoriteIds = {};
final FavoritesService _favoritesService = FavoritesService();
Future<void> _refreshFavoriteIds() async {
final ids = await _favoritesService.getFavoriteIds();
if (mounted) setState(() => _favoriteIds = ids);
}
// 加载作品后刷新收藏态
Future<void> _loadWorks() async {
// ... 请求与 setState ...
_refreshFavoriteIds();
}
// 从详情返回后刷新
void _openWorkDetail(WorkModel work) {
Navigator.of(context).push(...).then((_) => _refreshFavoriteIds());
}
Future<void> _toggleFavorite(WorkModel work) async {
await _favoritesService.toggleFavorite(work);
_refreshFavoriteIds();
}
// 卡片:心形可点击,红心=已收藏
_WorkCard(
work: work,
isFavorite: _favoriteIds.contains(work.id),
onTap: () => _openWorkDetail(work),
onFavoriteTap: () => _toggleFavorite(work),
)
// _WorkCard 内心形:IconButton 避免触发卡片 onTap
IconButton(
icon: Icon(
isFavorite ? Icons.favorite : Icons.favorite_border,
size: 14,
color: isFavorite ? Colors.red : Colors.grey[600],
),
onPressed: onFavoriteTap,
style: IconButton.styleFrom(
minimumSize: Size.zero,
padding: EdgeInsets.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
)
六、作品详情页(顶部收藏按钮)
文件:lib/pages/work_detail_page.dart
- 状态:
bool _isFavorite,initState里异步_favoritesService.isFavorite(widget.work.id)赋值并setState。 - 顶部栏:在返回键与页码之间增加收藏
IconButton,点击调用_toggleFavorite(),再setState(() => _isFavorite = !_isFavorite)。
关键代码片段:
bool _isFavorite = false;
final FavoritesService _favoritesService = FavoritesService();
void initState() {
super.initState();
// ...
_favoritesService.isFavorite(widget.work.id).then((v) {
if (mounted) setState(() => _isFavorite = v);
});
}
Future<void> _toggleFavorite() async {
await _favoritesService.toggleFavorite(widget.work);
if (mounted) setState(() => _isFavorite = !_isFavorite);
}
// 顶部 Row 中增加
IconButton(
icon: Icon(
_isFavorite ? Icons.favorite : Icons.favorite_border,
color: _isFavorite ? Colors.red : Colors.white,
),
onPressed: _toggleFavorite,
),
七、我的收藏页
文件:lib/pages/favorites_page.dart

- 进入时
_load()拉取_favoritesService.getFavoriteWorks(),展示列表;空列表显示「暂无收藏」;支持RefreshIndicator下拉刷新。 - 列表项点击
Navigator.push(WorkDetailPage(...)),返回后_load()再刷一次(以便在详情里取消收藏后列表同步)。
关键代码片段:
Future<void> _load() async {
setState(() => _loading = true);
final list = await _favoritesService.getFavoriteWorks();
if (mounted) setState(() {
_works = list;
_loading = false;
});
}
// 列表项点击进入详情,返回后刷新
onTap: () async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => WorkDetailPage(work: work, initialIndex: 0),
),
);
_load();
}
八、我的页入口
文件:lib/pages/profile_page.dart
- 引入
favorites_page.dart。 - 在功能入口区增加一项:图标
Icons.favorite,标题「我的收藏」,副标题「收藏的作品」,onTap调用_openFavorites()。 _openFavorites():Navigator.push(MaterialPageRoute(builder: (context) => const FavoritesPage()))。
关键代码片段:
import 'favorites_page.dart';
void _openFavorites() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const FavoritesPage(),
),
);
}
// 功能入口列表中加入
_MenuItem(
icon: Icons.favorite,
title: '我的收藏',
subtitle: '收藏的作品',
onTap: _openFavorites,
),
const Divider(height: 1),
_MenuItem(icon: Icons.history, title: '历史记录', ...),
九、使用方式
- 发现页:心形可点,红心=已收藏,灰心=未收藏;点击卡片进详情,返回后红心状态会更新。
- 作品详情页:顶部收藏图标可点,红=已收藏,白=未收藏。
- 我的 → 我的收藏:查看全部已收藏作品,点击进详情;在详情中取消收藏后返回,列表会少一条;下拉可刷新列表。
结束语
感谢阅读本帖,如对贴中内容有意见和建议的,欢迎与我联系交流,也欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)