Search code examples
jsonflutterdartsharedpreferencesstore

How to save custom "card" to Shared Preferences in dart/flutter?


I'm trying to save the progress AKA "cards". https://pub.dev/packages/fsrs

I can not serialize it, because it is not a map I guess.

Therefore the following code doesn't work.

import 'package:fsrs/fsrs.dart';
import 'dart:convert';

void main() {
  var f = FSRS();
  var card = Card();
  var now = DateTime(2022, 11, 29, 12, 30, 0, 0);
  print("Now: $now");
  var schedulingCards = f.repeat(card, now);
  // printSchedulingCards(schedulingCards);

var encoded = json.encode(card);
print(encoded);
}

This code gives: Rejecting promise with error: Converting object to an encodable object failed: Instance of 'Card'

I guess converting to json wouldn't be a bad idea, cause if maybe later I try to save it to firestore it would function well.

I can put a .toString() after the card, and that I can convert it to json, but I have no idea how to convert it back to "card".

import 'package:fsrs/fsrs.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';

void main() {
  var f = FSRS();
  var card = Card();
  var now = DateTime(2022, 11, 29, 12, 30, 0, 0);
  print("Now: $now");
  var schedulingCards = f.repeat(card, now);
  // printSchedulingCards(schedulingCards);

var encoded = json.encode(card.toString());
print(encoded);
}

this gives:

"{\"due\":\"2024-04-29 12:23:55.302Z\",\"stability\":0,\"difficulty\":0,\"elapsedDays\":0,\"scheduledDays\":0,\"reps\":0,\"lapses\":0,\"state\":\"State.newState\",\"lastReview\":\"2024-04-29 12:23:55.302Z\"}"

Please help me to get a card back from a stored state. Thanks!


Solution

  • If you are not a contributor to the package fsrc then you could use an extension on Card adding e.g. the getter toJson and a static method fromJson that returns a new instance of Card:

    import 'package:fsrs/fsrs.dart';
    import 'dart:convert';
    
    extension CardHelper on Card {
      /// Returns a map representation of this.
      Map<String, dynamic> get toJson => {
            'due': due.toIso8601String(),
            'stability': stability,
            'difficulty': difficulty,
            'elapsedDays': elapsedDays,
            'scheduledDays': scheduledDays,
            'reps': reps,
            'state': state.toString(),
            'lastReview': lastReview.toIso8601String()
          };
    
      /// Converts a map to an object of type `Card`.
      static Card fromJson(Map<String, dynamic> json) {
        // Validate input
        if (json
            case {
              'due': String due,
              'stability': double stability,
              'difficulty': double difficulty,
              'elapsedDays': int elapsedDays,
              'scheduledDays': int scheduledDays,
              'reps': int reps,
              'state': String state,
              'lastReview': String lastReview
            }) {
          return Card()
            ..due = DateTime.parse(due)
            ..stability = stability
            ..difficulty = difficulty
            ..elapsedDays = elapsedDays
            ..scheduledDays = scheduledDays
            ..reps = reps
            ..state = State.values
                .firstWhere((element) => element.toString() == state.toString())
            ..lastReview = DateTime.parse(lastReview);
        } else {
          throw JsonUnsupportedObjectError(json,
              cause: 'In CardHelper.fromJson: '
                  'Validation failed!');
        }
      }
    }
    
    void main() {
      final f = FSRS();
      final card = Card();
      final now = DateTime(2022, 11, 29, 12, 30, 0, 0);
      print("Now: $now");
      var schedulingCards = f.repeat(card, now);
    
      print(card);
      final jsonString = json.encode(card.toJson);
      final clonedCard = CardHelper.fromJson(json.decode(jsonString));
      print(clonedCard);
    }
    

    Below is the console output generated by running main:

    $ dart main.dart
    Now: 2022-11-29 12:30:00.000
    {"due":"2024-04-29 15:13:06.341169Z","stability":0.0,"difficulty":0.0,"elapsedDays":0,"scheduledDays":0,"reps":0,"lapses":0,"state":"State.newState","lastReview":"2024-04-29 15:13:06.341664Z"}
    {"due":"2024-04-29 15:13:06.341169Z","stability":0.0,"difficulty":0.0,"elapsedDays":0,"scheduledDays":0,"reps":0,"lapses":0,"state":"State.newState","lastReview":"2024-04-29 15:13:06.341664Z"}
    

    I would highly recommend creating a ticket requesting the authors of fsrs to add the methods toJson and a Card constructor Card.fromJson(Map<String, dynamic> json). This will solve your problem and will help other users of this package.