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

Flutter使用SharedPreferences保存DateTime遇序列化及空类型错误求助

解决SharedPreferences保存DateTime及序列化异常问题

问题原因

你遇到的两个异常本质都是DateTime无法直接JSON序列化/反序列化导致的:

  1. type 'Null' is not a subtype of type 'DateTime':加载数据时,jsonDecode无法将保存的DateTime对象还原为有效DateTime实例,返回Null,与Shopping类的非空date字段类型不匹配。
  2. Converting object to an encodable object failed: Instance of 'Shopping':保存数据时,Shopping的toJson方法直接返回DateTime实例,而jsonEncode不支持原生DateTime类型的序列化。

解决方案

通过将DateTime转换为JSON支持的数字类型(毫秒时间戳)来实现序列化与反序列化,同时优化数据加载逻辑。

1. 修改Shopping类,实现正确的序列化/反序列化

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';

class Shopping {
  String item;
  int price;
  DateTime date;
  Shopping({required this.item, required this.price, required this.date});

  // 序列化:将DateTime转为毫秒时间戳(JSON支持数字类型)
  Map<String, dynamic> toJson() {
    return {
      'item': item,
      'price': price,
      'date': date.millisecondsSinceEpoch,
    };
  }

  // 反序列化:从毫秒时间戳转回DateTime
  factory Shopping.fromJson(Map<String, dynamic> json) {
    return Shopping(
      item: json['item'] ?? '',
      price: json['price'] ?? 0,
      date: DateTime.fromMillisecondsSinceEpoch(json['date'] ?? 0),
    );
  }
}

2. 修改ItemData的loadData方法,使用fromJson构造函数

class ItemData extends ChangeNotifier {
  List<Shopping> listOfItem = [];
  
  // ... 其他方法保持不变 ...

  loadData() async {
    SharedPreferences pref = await SharedPreferences.getInstance();
    if (pref.getStringList('itemList') != null) {
      List<String> tempList = pref.getStringList('itemList')!;
      for (int i = 0; i < tempList.length; i++) {
        Map<String, dynamic> temp = jsonDecode(tempList[i]);
        // 使用fromJson构造Shopping实例,避免类型不匹配
        addItem(Shopping.fromJson(temp));
      }
    }
  }
}

3. 优化AddItem页面的日期获取逻辑

将DateTime的获取移到按钮点击事件内,避免build方法重复创建实例,同时确保保存操作完成后再返回上一页:

CupertinoButton(
  // ... 其他属性保持不变 ...
  onPressed: () async {
    // 点击时才获取当前时间
    DateTime date = DateTime.now();
    Shopping newItem = Shopping(
      item: userInputItem.text,
      price: int.parse(userInputPrice.text),
      date: date,
    );
    provider.addItem(newItem);
    // 等待保存完成再返回,避免数据未写入就退出
    await provider.saveData();
    Navigator.pop(context);
  },
),

关键说明

  • 毫秒时间戳是JSON原生支持的数字类型,能被jsonEncode/jsonDecode正确处理,避免序列化异常。
  • 使用factory Shopping.fromJson统一处理反序列化逻辑,同时通过??运算符处理可能的空值,防止类型转换失败。
  • 保存数据时添加await,确保SharedPreferences完成写入操作后再执行页面跳转,避免数据丢失。

内容的提问来源于stack exchange,提问作者Abdulhadi Naser

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 04:55:28