0

I have the following class setup. I was able to store the JSON, but I'm having trouble retrieving it.

import 'package:flutter/material.dart';

class SvItem with ChangeNotifier {
  final String id;
  final String meatType;
  final String meatTypeImg;
  final String meatCut;
  final String duration;
  final String temperatur;
  final List<String> instructions;
  final String notes;
  bool isFavorite;

  SvItem({
    required this.id,
    required this.meatType,
    required this.meatTypeImg,
    required this.meatCut,
    required this.duration,
    required this.temperatur,
    required this.instructions,
    required this.notes,
    this.isFavorite = false,
  });

  factory SvItem.fromJson(Map<String, SvItem> json) {
    return SvItem(
      id: "id",
      meatType: "meatType",
      meatTypeImg: "meatTypeImg",
      meatCut: "meatCut",
      duration: "duration",
      temperatur: "temperatur",
      instructions: "instructions" as List<String>,
      notes: "notes",
      isFavorite: "isFavorite" as bool,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      "id": id,
      "meatType": meatType,
      "meatTypeImg": meatTypeImg,
      "meatCut": meatCut,
      "duration": duration,
      "temperatur": temperatur,
      "instructions": instructions,
      "notes": notes,
      "isFavorite": isFavorite
    };
  }
}

With this code, I can save instances of the the custom class in sharedpreferences.

Future saveCustomRecipes(item) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  prefs.setString("customRecipesList", jsonEncode(item));
  setState(() {});
}

saveCustomRecipes(customRecipes);

But how can I retrieve it and save it in customRecipes?

I tried the following:

  getCustomRecipesValues() async {
    customRecipes = await getCustomRecipesState();
    setState(() {});
  }

  Future getCustomRecipesState() async {
    final prefs = await SharedPreferences.getInstance();
    var recipeData = prefs.getString("customRecipesList");
    List<SvItem> customRecipes =
        SvItem.fromJson(jsonDecode(recipeData!)) as List<SvItem>;

    print(customRecipes);
    return customRecipes;
  }

But I get an error: [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'List' is not a subtype of type 'Map<String, dynamic>'

quik
  • 1
  • 2

3 Answers3

0

when you use jsonDecode(recipeData!) it will get a Map not a List.

try convert the code :

List<SvItem> customRecipes =
    SvItem.fromJson(jsonDecode(recipeData!)) as List<SvItem>;

into this :

Map<String, dynamic> customRecipes =
    SvItem.fromJson(jsonDecode(recipeData!));

also why did you use jsonEncode not the method toJson from your model class?

  • yeah, true that part in my class is redundant. I was trying different ways to get it to work. When I try your suggestions, I get: [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'List' is not a subtype of type 'Map' – quik Mar 27 '23 at 15:08
0

You said that you pass a List<SvItem> to the function. So you can loop over the list and turn each item into json. Then you add the json objects to another list and encode it.

Try this:

Future saveCustomRecipes(List<SvItem> items) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  sv_items = [];
  for(SvItem item in items)
  {
     sv_items.add(item.toJson());
  }
  await prefs.setString("customRecipesList", jsonEncode(sv_items));
}

and

Future getCustomRecipesState() async {
    final prefs = await SharedPreferences.getInstance();
    var recipeData = prefs.getString("customRecipesList");
    List<Map<String, dynamic>> items = jsonDecode(recipeData!) as List<Map<String, dynamic>>;
    List<SvItem> customRecipes = [];
    for(Map<String, dynamic> item in items)
    {
       customRecipes.add(SvItem.fromJson(item));
    }
    print(customRecipes);
    return customRecipes;
}
Code Master
  • 437
  • 1
  • 7
  • that makes sense. But now I have this issue: customRecipes.add(SvItem.fromJson(item)); item shows this error: "The argument type 'SvItem?' can't be assigned to the parameter type 'Map'" Also when I typecast item as Map I get a compiling error: "A value of type 'Map' can't be assigned to a variable of type 'SvItem?'. addRecipe.dart:115 - 'Map' is from 'dart:core'." – quik Mar 28 '23 at 06:56
  • If I cast item in the for loop like this: for (Map item in items) it does compile. But then I get an error in the console: [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'List' is not a subtype of type 'List>' – quik Mar 28 '23 at 07:38
  • Sorry, I made a little mistake here. You need to to put the type into the for loop. I just edited my answer. Can you please check if it works now? – Code Master Mar 28 '23 at 16:32
  • I still get the error: [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'List' is not a subtype of type 'List>' for: List> items = jsonDecode(recipeData!); – quik Mar 29 '23 at 18:24
  • Maybe it has to do with my SvItem class, because all properties are String, except 2. One is a List and another one is a bool. Maybe that's where the issue is, and I need a nested for loop to go over each String in the List for the property "instructions". – quik Mar 29 '23 at 18:29
  • Try to cast the jsonDecode object. Because it returns a List, you can't assign it to a variable with type List>. Can you please try again and check if it works now? – Code Master Mar 30 '23 at 14:42
  • thank you for your help, but unfortunately I still get an error: [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'List' is not a subtype of type 'List>' in type cast – quik Apr 01 '23 at 08:06
0

ok I believe I managed to find a solution: This is my SvItem Class:

class SvItem with ChangeNotifier {
  final String id;
  final String meatType;
  final String meatTypeImg;
  final String meatCut;
  final String duration;
  final String temperatur;
  final List<String> instructions;
  final String notes;
  bool isFavorite;

  SvItem({
    required this.id,
    required this.meatType,
    required this.meatTypeImg,
    required this.meatCut,
    required this.duration,
    required this.temperatur,
    required this.instructions,
    required this.notes,
    this.isFavorite = false,
  });

  factory SvItem.fromJson(dynamic json) {
    return SvItem(
      id: json["id"] as String,
      meatType: json["meatType"] as String,
      meatTypeImg: json["meatTypeImg"] as String,
      meatCut: json["meatCut"] as String,
      duration: json["duration"] as String,
      temperatur: json["temperatur"] as String,
      instructions: List.from(json["instructions"] as List),
      notes: json["notes"] as String,
      isFavorite: json["isFavorite"] as bool,
    );
  }

  Map toJson() {
    return {
      "id": id,
      "meatType": meatType,
      "meatTypeImg": meatTypeImg,
      "meatCut": meatCut,
      "duration": duration,
      "temperatur": temperatur,
      "instructions": instructions,
      "notes": notes,
      "isFavorite": isFavorite,
    };
  }
}

This is the part to store and retrieve in SP:

  List<SvItem> customRecipes = [];
  String customRecipeString = "";
  XFile? image;
  SvItem? item;

  @override
  void initState() {
    getStringState().then((value) {
      setState(() {
        List customRecipesJsonList = jsonDecode(value);
        for (var item in customRecipesJsonList) {
          SvItem customItem = SvItem.fromJson(jsonDecode(item));
          var contain =
              customRecipes.where((element) => element.id == customItem.id);
          if (contain.isEmpty) {
            customRecipes.add(customItem);
          }
        }
      });
    });

    super.initState();
  }

  Future<Future<bool>> saveStringState(String string) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString("customRecipes", string);
    return prefs.setString("customRecipes", string);
  }

  Future<String> getStringState() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String? customRecipeString = prefs.getString("customRecipes");
    return customRecipeString as String;
  }

  getStringValues() async {
    customRecipeString = await getStringState();
    setState(() {});
  }

And this let's me save to SP and also decode it again:

                  customRecipes.add(item);
                  List customRecipesJson = [];
                  for (SvItem item in customRecipes) {
                    String jsonCustomItem = jsonEncode(item);
                    customRecipesJson.add(jsonCustomItem);
                  }
                  String customRecipesJsonString =
                      jsonEncode(customRecipesJson);
                  saveStringState(customRecipesJsonString);
                  print(customRecipesJsonString);

                  List customRecipesJsonList =
                      jsonDecode(customRecipesJsonString);
                  for (var item in customRecipesJsonList) {
                    SvItem customItem =
                        SvItem.fromJson(jsonDecode(item));
                    var contain = customRecipes.where(
                        (element) => element.id == customItem.id);
                    if (contain.isEmpty) {
                      customRecipes.add(customItem);
                    }
                  }
quik
  • 1
  • 2