这里带大家一起交流学习下在鸿蒙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.yamldependencies 下增加:

  # 本地持久化(收藏、主题、搜索历史等)
  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> _favoriteIdsFavoritesService _favoritesService
  • 进入页面:initState_loadWorks() 后调用 _refreshFavoriteIds();从详情返回时 _openWorkDetailNavigator.push(...).then((_) => _refreshFavoriteIds()),保证列表红心与详情内操作一致。
  • 卡片:_WorkCard 增加 isFavoriteonFavoriteTap;心形用 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 _isFavoriteinitState 里异步 _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: '历史记录', ...),

九、使用方式

  1. 发现页:心形可点,红心=已收藏,灰心=未收藏;点击卡片进详情,返回后红心状态会更新。
  2. 作品详情页:顶部收藏图标可点,红=已收藏,白=未收藏。
  3. 我的我的收藏:查看全部已收藏作品,点击进详情;在详情中取消收藏后返回,列表会少一条;下拉可刷新列表。

结束语

感谢阅读本帖,如对贴中内容有意见和建议的,欢迎与我联系交流,也欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐