Firebase document withConverter where documents are not homogenous fails

55 views Asked by At

I have a collection in Firestore in which its documents are not homogenous (a field may or may not exist from document to document). I have attempted several ways of allowing nullable values for those keys, but I regularly get an exception thrown during fromJson on the object.

Is it not possible to use withConverter unless you have homogenous documents?

In this pared down example, isAdmin would be non-existent on documents relating to non-admin users.

class UserDocument {
  const UserDocument({
    required this.isAdmin,
    required this.location,
  });

  final bool? isAdmin;
  final String location;

  UserDocument.fromJson(Map<String, Object?> json)
      : this(
          isAdmin: json['is_admin'] as bool,
          location: json['location']! as String,
        );

  Map<String, Object?> toJson() => {
        'is_admin': isAdmin,
        'location': location,
      };
}

DocumentReference collectionDoc(String collectionName, String documentName) {
  return FirebaseFirestore.instance.collection(collectionName).doc(documentName);
}

Stream<DocumentSnapshot<UserDocument>> userStream() {
  final dataRef = collectionDoc("user", currentAuthUid).withConverter<UserDocument>(
      fromFirestore: (snapshots, _) => UserDocument.fromJson(snapshots.data()!), toFirestore: (user, _) => user.toJson());
  return dataRef.snapshots();
}

This is the error trace that results

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following TypeErrorImpl was thrown building StreamBuilder<DocumentSnapshot<UserDocument>>(dirty,
state: _StreamBuilderBaseState<DocumentSnapshot<UserDocument>,
AsyncSnapshot<DocumentSnapshot<UserDocument>>>#3621a):
Unexpected null value.

The relevant error-causing widget was:
  StreamBuilder<DocumentSnapshot<UserDocument>>
1

There are 1 answers

0
cmjordan On BEST ANSWER

The null checks on json['key'] were not helpful. This works when you declare the variable as nullable...

final bool? isAdmin;
final String? location;

and in the converter...

UserDocument.fromJson(Map<String, Object?> json)
      : this(
          isAdmin: json['is_admin'] as bool?,
          location: json['location'] as String?,
        );

and then null-check where you may reference these data in your code.