3

I'm about to use the database "Sembast" in Flutter. Simple objects with data types like string and int are working properly. However, it becomes problematic when using Lists.

I have created an example and oriented myself on the following tutorial: https://resocoder.com/2019/04/06/flutter-nosql-database-sembast-tutorial-w-bloc/ In my example, there are fruits and leaves as objects. A fruit contains a list of leaves.

class Fruit {
  final String id;
  final String name;
  final bool isSweet;
  final List<Leaves> leaves;
...
}

class Leaves {
  final String id;
  final String name;
...
}

//Create a sample object
var leaveOne = Leaves(id: "1", name: "leaveOne");
var leaveTwo = Leaves(id: "2", name: "leaveTwo");
var leaveThree = Leaves(id: "3", name: "leaveThree");

var leavesList = List<Leaves>();
leavesList.add(leaveOne);
leavesList.add(leaveTwo);
leavesList.add(leaveThree);

var fruit = Fruit(id: "1", name: "Apple", isSweet: true, leaves: leavesList);
_fruitDao.insert(fruit);


// The fruitDao.insert makes following
Future insert(Fruit fruit) async {
  await _fruitStore.add(await _db, fruit.toJson());
}

The JSON looks like that: {id: 1, name: Apple, isSweet: true, leaves: [Instance of 'Leaves', Instance of 'Leaves', Instance of 'Leaves']}

The ERROR is following: [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Invalid argument(s): value Instance of 'Leaves' unsupported type Leaves

3 Answers 3

7

As pointed at, Instance of 'Leaves' is not a valid type so each Leave must be converted as well. Hard to guess what you are doing without seeing your toJson() but something like this should work (could be largely optimized):

class Fruit {
  final String id;
  final String name;
  final bool isSweet;
  final List<Leaves> leaves;

  Fruit({this.id, this.name, this.isSweet, this.leaves});

  Map<String, dynamic> toJson() => <String, dynamic>{
        'id': id,
        'name': name,
        'isSweet': isSweet,
        'leaves': leaves?.map((leave) => leave.toJson())?.toList(growable: false)
      };
}

class Leaves {
  final String id;
  final String name;

  Leaves({this.id, this.name});

  Map<String, dynamic> toJson() => <String, dynamic>{'id': id, 'name': name};
}

and your json should look like something this this:

{
  "id": "1",
  "name": "Apple",
  "isSweet": true,
  "leaves": [
    {
      "id": "1",
      "name": "leaveOne"
    },
    {
      "id": "2",
      "name": "leaveTwo"
    },
    {
      "id": "3",
      "name": "leaveThree"
    }
  ]
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for reply! I'm using "json_serializable" - package. So this generates me the code for the function toJson() and fromJson(). If I edit this generated file (usually you should not do this), and paste your code to map the leaves ('leaves': leaves?.map((leave) => leave.toJson())?.toList(growable: false)) - it's working fine.
3

Here is an example in addition to @alextk answer with converting to and from without any code generation or library's.

class Fruit {
  final String id;
  final String name;
  final bool isSweet;
  final List<Leaves> leaves;

  Fruit({this.id, this.name, this.isSweet, this.leaves});

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
      'isSweet': isSweet,
      'leaves': leaves.map((leave) => leave.toMap()).toList(growable: false)
    };
  }

  static Fruit fromMap(Map<String, dynamic> map) {
    return Fruit(
      id: map['id'],
      name: map['name'],
      isSweet: map['isSweet'],
      leaves: map['leaves'].map((mapping) => Leaves.fromMap(mapping)).toList().cast<Leaves>(),
    );
  }
}

class Leaves {
  final String id;
  final String name;

  Leaves({this.id, this.name});

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
    };
  }

  static Leaves fromMap(Map<String, dynamic> map) {
    return Leaves(
      id: map['id'],
      name: map['name'],
    );
  }
}

1 Comment

Instead of using a static fromMap method you can very well use a Factory method
2

For cases when you use freezed, I found this solution:

...
...
@freezed
class Fruit with _$Fruit {
//add this line
@JsonSerializable(explicitToJson: true)
const factory Fruit({...}) = _Fruit;

factory Fruit.fromJson(Map<String, dynamic> json) => _$FruitFromJson(json);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.