You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 21:15:02