Home > Net >  How can we save the data from a complex object in Firebase Firestore in Flutter?
How can we save the data from a complex object in Firebase Firestore in Flutter?

Time:03-17

I use complex objects to manage data in my app. For example I have an object defined by the "Defi class" (meaning Challenge in French).

Here is the Defi class :

class Defi {
  final int modeApprentissage;
  final DateTime date;
  final double score;
  final int seconds;
  final List mots;
  final List erreurs;
  final List listes;
  final int langue;
  final int type;
  final int typeMots1;
  final int typeMots2;
  const Defi(
      {required this.modeApprentissage,
      required this.date,
      required this.type,
      required this.langue,
      required this.typeMots1,
      required this.typeMots2,
      required this.score,
      required this.seconds,
      required this.listes,
      required this.mots,
      required this.erreurs});
}

I have a LIST of Defi that I would like to save on FIREBASE FIRESTORE. My question : Is it absolutely necessary to transform my list of Defi into a map to save it on Firestore ? Or is there another way ?

Here is how I do it :

 List _defisMap = [];
    for (Defi def in _defis) {
      _defisMap.add({
        'modeApprentissage': def.modeApprentissage,
        'type': def.type,
        'langue': def.langue,
        'typeMots1': def.typeMots1,
        'typeMots2': def.typeMots2,
        'date': def.date,
        'score': def.score,
        'seconds': def.seconds,
        'listes': def.listes,
        'mots': def.mots,
        'erreurs': def.erreurs,
      });
    }
    if (await _connectivity.checkConnectivity() != ConnectivityResult.none) {
      await FirebaseFirestore.instance
          .collection('familyAccounts')
          .doc(_accountEmail)
          .update({
        'defis': _defisMap,
      });

I read in some article that in classes such as Defi, I could add a "factory" constructor ? Does this have anything to do with what I'd like to do ?

I created another class :

class Classes {
  final String code;
  final String nom;

  const Classes({
    required this.code,
    required this.nom,
  });

  Map<String, dynamic> toJson() => {
        'code': code,
        'nom': nom,
      };


  factory Classes.fromMap(Map data) => Classes(
        code: data['code'] ?? '',
        nom: data['nom'] ?? '',
      );
}

I save a list of Classes on Firestore. No problem. But to retrieve this list : I must go from the list of maps that is on Firestore to a list of "Classes". And I just can't get the syntax right !

Here is my code :

final DocumentSnapshot<Map<String, dynamic>> docInfo =
        await FirebaseFirestore.instance
            .collection('familyAccounts')
            .doc(_accountEmail)
            .get();

    _typeCompte =
        docInfo['typeCompte'] == 'prof' ? TypeCompte.prof : TypeCompte.eleve;
    _userId = docInfo['userId'];
    _linked = docInfo['linked'];
    _name = docInfo['name'];
    _avatar = docInfo['avatar'];
    _classe = docInfo['classe'];
    _classeCode = docInfo['classeCode'];
    _country = docInfo['country'];
    _region = docInfo['region'];
    docInfo['langue'] == 'french'
        ? _selectedIoLanguage = Language.french
        : _selectedIoLanguage = Language.english;
    _teacherCode = docInfo['teacherCode'];
    _indexList = docInfo['indexList'];
    _nbrList = docInfo['nbrList'];
    _dateCreationCompte = docInfo['dateCreation'].toDate();
    _defiTemp = docInfo['defis'].toList();

    if (_typeCompte == TypeCompte.prof) {
      _etablissement = docInfo['etablissement'];
      _mesClasses = docInfo['mesClasses'];

(_mesClasses should be a list of "Classes").

I sense it should be some kind of xxx.map() etc.... but I don't master this syntax.

CodePudding user response:

You need to create toJson method to set as a map of your list. If you have a list of Defi class. You can send it to map.

class Defi {
  final int modeApprentissage;
  final DateTime date;
  final double score;
  final int seconds;
  final List mots;
  final List erreurs;
  final List listes;
  final int langue;
  final int type;
  final int typeMots1;
  final int typeMots2;
  const Defi(
      {required this.modeApprentissage,
      required this.date,
      required this.type,
      required this.langue,
      required this.typeMots1,
      required this.typeMots2,
      required this.score,
      required this.seconds,
      required this.listes,
      required this.mots,
      required this.erreurs});

Map<String, dynamic> toJson() => {
       'modeApprentissage': modeApprentissage,
        'type': type,
        'langue': langue,
        'typeMots1': typeMots1,
        'typeMots2': typeMots2,
        'date': date,
        'score': score,
        'seconds': seconds,
        'listes': listes,
        'mots': mots,
        'erreurs': erreurs,
      };
}

Your list name is should be List<Defi> _defis then map it with toJson. var jsonMap = _defisMap.map((e) => e.toJson()).toList();


var jsonMap = _defis.map((e) => e.toJson()).toList();

if (await _connectivity.checkConnectivity() != ConnectivityResult.none) {
      await FirebaseFirestore.instance
          .collection('familyAccounts')
          .doc(_accountEmail)
          .update({
        'defis': jsonMap,
      });

You can also Map it from api call with fromJson method. Here is the way. Add this to your Defi class.

factory Defi.fromJson(Map<String, dynamic> json) {
    return Defi(
        modeApprentissage: json['modeApprentissage'],
        type: json['type'],
        langue: json['langue'],
        typeMots1: json['typeMots1'],
        typeMots2: json['typeMots2'],
        date: json['date'],
        score: json['score'],
        seconds: json['seconds'],
        listes: json['listes'],
        mots: json['mots'],
        erreurs: json['erreurs'],);
  }

And when you call api you need to call that function.

final Map<String, dynamic> jsonResult = json.decode(response.body);


//if it is a list returning from api. You can change variables what you got from api.


(jsonResult as List<dynamic>)
          .map((data) => Defi.fromJson(data))
          .toList();

// if it is not list 

Defi.fromJson(jsonResult);


CodePudding user response:

Yes, it's absolutely necessary to transform into a map. Firestore is a document store, the definition of a document is a json object, which is represented by a map in Dart. BUT, you don't have to do it "manually". Most of us use packages to do the mapping for us.

Freezed or JsonSerializable and then we simply call .toJson and pass that to the function. In addition to that Firestore now supports the .withConverter function so you don't have to do any conversion if you know what type the collection is storing.

So if you know the type of a collection is Defi you can do

   final defyCollection = FirebaseFirestore.instance.collection('defis').withConverter<Defi>(
        fromFirestore: (snapshot, _) {
          final data = snapshot.data()!;
          data['id'] = snapshot.id;
          return Defi.fromJson(data);
        },
        toFirestore: (object, _) => object.toJson(),
      )

This way you can simply use the defyCollection and the data property or function will be typed to your type.

  • Related