Flutter如何将含非基本类型字段的自定义类列表存入SharedPreferences
问题原因
- 类型不匹配错误:你存储的是
List<String>类型(每个元素是单个Medicament对象序列化后的JSON字符串),但读取时直接将List<String>强转为Map<String, dynamic>传入MedicamentsBase.fromJson方法,类型完全不匹配,必然会抛出转换异常。同时MedicamentsBase要求传入包含data字段的JSON结构,你存储的结构也不符合该类的反序列化要求。 - DateTime序列化未配置:
json_serializable默认不会自动处理DateTime类型的序列化与反序列化,若不手动配置转换逻辑,序列化时会直接输出Instance of 'DateTime'这类无效内容,反序列化也会失败。
正确实现步骤
1. 配置DateTime类型转换逻辑
在Medicament类的所有DateTime类型字段上添加序列化转换配置,示例如下:
// 顶层转换方法,也可以配置为全局转换器 DateTime _dateTimeFromJson(int timestamp) => DateTime.fromMillisecondsSinceEpoch(timestamp); int _dateTimeToJson(DateTime date) => date.millisecondsSinceEpoch; List<DateTime> _dateTimeListFromJson(List<dynamic> timestampList) => timestampList.map((e) => DateTime.fromMillisecondsSinceEpoch(e as int)).toList(); List<int> _dateTimeListToJson(List<DateTime> dateList) => dateList.map((e) => e.millisecondsSinceEpoch).toList(); @JsonSerializable() class Medicament { @JsonKey(name: '_id') String id; String name; int dosageAmount; String dosageType; // 给DateTime字段加转换配置 @JsonKey(fromJson: _dateTimeFromJson, toJson: _dateTimeToJson) DateTime startDate; @JsonKey(fromJson: _dateTimeFromJson, toJson: _dateTimeToJson) DateTime endDate; // 给DateTime列表字段加转换配置 @JsonKey(fromJson: _dateTimeListFromJson, toJson: _dateTimeListToJson) List<DateTime> timesOfDay; String intakeContext; bool notify; int notificationId; // 原有构造、fromJson、toJson方法保持不变 }
配置完成后执行命令重新生成序列化代码:flutter pub run build_runner build --delete-conflicting-outputs
2. 存储与读取逻辑实现
这里推荐直接使用你已有的MedicamentsBase封装类,整体序列化后存储为单个字符串,逻辑更简洁:
存储方法
static Future<void> setMedicaments(List<Medicament> medicaments) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); // 封装为MedicamentsBase对象后整体序列化为JSON字符串 final baseObj = MedicamentsBase(data: medicaments); final jsonString = jsonEncode(baseObj.toJson()); await prefs.setString('medicament_data_key', jsonString); }
读取方法
static Future<List<Medicament>> getMedicaments() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); final String? jsonString = prefs.getString('medicament_data_key'); // 空值判断,无数据时返回空列表 if (jsonString == null) return []; // 先把字符串转为Map,再反序列化为对象 final Map<String, dynamic> jsonMap = jsonDecode(jsonString) as Map<String, dynamic>; return MedicamentsBase.fromJson(jsonMap).data; }
可选方案:保留StringList存储逻辑
如果你坚持要存储为字符串列表,不需要用到MedicamentsBase,修改读取逻辑即可:
static Future<List<Medicament>> getMedicaments() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); final List<String>? medicamentStrList = prefs.getStringList('key'); if (medicamentStrList == null) return []; return medicamentStrList.map((str) { final Map<String, dynamic> jsonMap = jsonDecode(str) as Map<String, dynamic>; return Medicament.fromJson(jsonMap); }).toList(); }
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

