Иногда появляется необходимость сохранять значения не только примитивных типов, но и целые объекты.
Подготовка класса
Имеется некоторый класс CounterInfo с набором свойств value типа int, lastUpdate типа DateTime и userName типа String.
class CounterInfo {
  int value;
  DateTime lastUpdate;
  String userName;
  CounterInfo({
    required this.value,
    required this.lastUpdate,
    required this.userName,
  });
}
Для того чтобы сохранить объект данного типа в локальное хранилище необходимо в данный класс добавить метод toJson(), который будет возвращать Map<String, dynamic>. Ключами в данном словаре будут названия свойств типа, а значениями, соответственно, значения хранящиеся в свойствах объекта.
  Map<String, dynamic> toJson() => {
        'value': value,
        'lastUpdate': lastUpdate.toIso8601String(),
        'userName': userName,
      };
Далее необходимо создать именованный конструктор fromJson(). C его помощью из Map<String, dynamic> будет создаваться экземпляр класса CounterInfo.
  CounterInfo.fromJson(Map<String, dynamic> json)
      : value = json['value'],
        lastUpdate = DateTime.parse(json['lastUpdate']),
        userName = json['userName'];
В итоге класс CounterInfo будет выглядеть следующим образом:
class CounterInfo {
  int value;
  DateTime lastUpdate;
  String userName;
  CounterInfo({
    required this.value,
    required this.lastUpdate,
    required this.userName,
  });
  CounterInfo.fromJson(Map<String, dynamic> json)
      : value = json['value'],
        lastUpdate = DateTime.parse(json['lastUpdate']),
        userName = json['userName'];
  Map<String, dynamic> toJson() => {
        'value': value,
        'lastUpdate': lastUpdate.toIso8601String(),
        'userName': userName,
      };
}
Использование пакета shared_preferences
Для сохранения экземпляра класса CounterInfo используется json.encode(), который позволяет конвертировать объект в JSON строку. После этого результат выполнения json.encode() можно сохранить, используя метод SharedPreferences setString():
Future _setCounterInfo() async {
  var prefs = await SharedPreferences.getInstance();
  final counterInfo = CounterInfo(
    value: _counter,
    lastUpdate: DateTime.now(),
    userName: 'Alex',
  );
  prefs.setString(counterInfoKey, json.encode(counterInfo));
}
Для получения объекта необходимо воспользоваться методом SharedPreferences getString() и полученный результат передать методу json.decode(), чтобы из JSON строки получить JSON объект. Далее, данный объект передается в именованный конструктор fromJson() и на выходе получается новый объект типа CounterInfo:
  Future<CounterInfo?> _getCounterInfo() async {
    var prefs = await SharedPreferences.getInstance();
    final counterInfo = prefs.getString(counterInfoKey);
    if (counterInfo == null) return null;
    return CounterInfo.fromJson(json.decode(counterInfo));
  }
Полезные ссылки:
Как использовать shared preferences
Как сохранять DateTime используя shared preferences
Телеграм: https://t.me/the_cybermania
Исходный код: https://github.com/AlexeyShpavda/shared_preferences_objects
Видео: https://www.youtube.com/watch?v=HKwzU6Owxjs