如何使用SharedPreferences解码对象列表并持久化收藏餐品?
餐品收藏状态持久化问题修复
问题背景
已基于json_serializable实现Meal模型,通过FavouritesCubit结合SharedPreferences实现餐品收藏操作,但应用重启后收藏状态丢失,需解决Meal列表的编码/解码持久化问题。
现有代码
Meal模型(meal.dart)
import 'dart:convert'; import 'package:json_annotation/json_annotation.dart'; part 'meal.g.dart'; List<Meal> mealExtFromJson(String str) => List<Meal>.from(jsonDecode(str).map((x) => Meal.fromJson(x))); String mealExtToJson(List<Meal> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson()))); enum Complexity { @JsonValue(203) simple, @JsonValue(204) challenging, @JsonValue(205) hard, } enum Affordability { @JsonValue(200) affordable, @JsonValue(201) pricey, @JsonValue(202) luxurious, } @JsonSerializable() class Meal { final String id; final List<String> categories; final String title; final String imageUrl; final List<String> ingredients; final List<String> steps; final int duration; final Complexity complexity; final Affordability affordability; final bool isGlutenFree; final bool isLactoseFree; final bool isVegan; final bool isVegetarian; const Meal({ required this.id, required this.categories, required this.title, required this.imageUrl, required this.ingredients, required this.steps, required this.duration, required this.complexity, required this.affordability, required this.isGlutenFree, required this.isLactoseFree, required this.isVegan, required this.isVegetarian, }); factory Meal.fromJson(Map<String, dynamic> json) => _$MealFromJson(json); Map<String, dynamic> toJson() => _$MealToJson(this); }
FavouritesCubit(favourites_cubit.dart)
import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:meals_app/models/dummy_data.dart'; import 'package:meals_app/models/meal.dart'; import 'package:shared_preferences/shared_preferences.dart'; part 'favourites_state.dart'; class FavouritesCubit extends Cubit<FavouritesState> { final String favouriteDataPref = 'favouriteData'; List<Meal> favourites = []; SharedPreferences? _prefs; FavouritesCubit() : super(const FavouritesInitial()) { SharedPreferences.getInstance().then((prefs) { _prefs = prefs; loadSavedFavourites(prefs); }); } void toggleFavourite(String newMealId) { final existingIndex = favourites.indexWhere((meal) => meal.id == newMealId); if (existingIndex == -1) { favourites.add( DummyData().mealsData.firstWhere((meal) => meal.id == newMealId)); } else { favourites.removeAt(existingIndex); } unawaited(saveFavourites(favourites).onError( (error, stackTrace) => print(stackTrace), )); emit( FavouritesLoaded( favouritedMeals: favourites, ), ); } Future<void> saveFavourites(List<Meal> favouritedList) async { favourites = favouritedList; await _prefs?.setString(favouriteDataPref, mealExtToJson(favouritedList)); } loadSavedFavourites(SharedPreferences prefs) async { String? favouritedList = prefs.getString(favouriteDataPref); try { if (favouritedList != null) { List<Meal> savedFavs = mealExtFromJson(favouritedList); favourites = savedFavs; return; } } catch (e) { print(e); } } }
问题分析
- 加载数据后未触发状态更新:
loadSavedFavourites方法加载完收藏数据后,没有调用emit更新UI状态,导致重启后UI无法感知已保存的收藏列表。 - SharedPreferences初始化异步风险:构造函数中通过
then处理初始化,可能存在操作执行时_prefs仍为null的情况,导致保存失败。 - 错误处理不完整:保存和加载过程中的异常未妥善处理,可能导致数据丢失或状态不一致。
修复方案
修改后的FavouritesCubit代码
import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:meals_app/models/dummy_data.dart'; import 'package:meals_app/models/meal.dart'; import 'package:shared_preferences/shared_preferences.dart'; part 'favourites_state.dart'; class FavouritesCubit extends Cubit<FavouritesState> { final String favouriteDataPref = 'favouriteData'; List<Meal> favourites = []; SharedPreferences? _prefs; FavouritesCubit() : super(const FavouritesInitial()) { _initPreferences(); } // 统一初始化SharedPreferences并加载收藏数据 Future<void> _initPreferences() async { _prefs = await SharedPreferences.getInstance(); await _loadSavedFavourites(); } void toggleFavourite(String newMealId) { final existingIndex = favourites.indexWhere((meal) => meal.id == newMealId); if (existingIndex == -1) { favourites.add( DummyData().mealsData.firstWhere((meal) => meal.id == newMealId)); } else { favourites.removeAt(existingIndex); } unawaited(_saveFavourites().onError( (error, stackTrace) => print('保存收藏失败: $error\n$stackTrace'), )); emit(FavouritesLoaded(favouritedMeals: List.from(favourites))); } // 保存收藏列表到SharedPreferences Future<void> _saveFavourites() async { if (_prefs == null) { throw Exception('SharedPreferences未完成初始化'); } await _prefs!.setString(favouriteDataPref, mealExtToJson(favourites)); } // 加载已保存的收藏列表并更新状态 Future<void> _loadSavedFavourites() async { try { final favouritedList = _prefs?.getString(favouriteDataPref); if (favouritedList != null) { favourites = mealExtFromJson(favouritedList); } emit(FavouritesLoaded(favouritedMeals: List.from(favourites))); } catch (e) { print('加载收藏数据失败: $e'); emit(const FavouritesLoaded(favouritedMeals: [])); } } }
关键修复点
- 统一初始化流程:用
_initPreferences异步方法处理SharedPreferences初始化和数据加载,避免异步回调导致的状态不一致。 - 加载后触发状态更新:
_loadSavedFavourites方法在加载完成(或出错)后,通过emit更新UI状态,确保重启后收藏列表能正确显示。 - 空安全处理:保存数据时先检查
_prefs是否为空,避免空指针异常。 - 状态数据不可变:emit时传递
List.from(favourites)创建新列表,确保Bloc状态的不可变性。 - 增强错误日志:为保存和加载过程添加更清晰的错误提示,便于排查问题。
额外验证步骤
确保已运行build_runner生成序列化代码:
flutter pub run build_runner build
内容的提问来源于stack exchange,提问作者darlingtonhp
相关产品推荐
相关产品推荐

