Skip to content

[cloud_firestore] PlatformException when writing data to firestore on Android #2101

Closed
@Laebrye

Description

@Laebrye

I created a new project using the latest version of the cloud_firestore plugin. It uses anonymous sign in (which works) and writes the data to the database. All works as expected on iOS simulator. Ran on Android physical device (Huawei Mate20 pro) but got the following error when attempting to write data to the DB.

E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): Failed to handle method call
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): java.lang.NullPointerException: Provided data must not be null.
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:906)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at com.google.firebase.firestore.DocumentReference.set(com.google.firebase:firebase-firestore@@21.3.0:161)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at com.google.firebase.firestore.DocumentReference.set(com.google.firebase:firebase-firestore@@21.3.0:146)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin.onMethodCall(CloudFirestorePlugin.java:828)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:231)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:93)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:642)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at android.os.MessageQueue.next(MessageQueue.java:386)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at android.os.Looper.loop(Looper.java:175)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at android.app.ActivityThread.main(ActivityThread.java:7625)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
E/MethodChannel#plugins.flutter.io/cloud_firestore( 7665): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)

My code - error triggered on the addData method:

import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/foundation.dart';
import 'dart:async';


class AppData {
  static final Firestore _firestore = Firestore.instance;

  AppData(
      {@required this.collectionPath,
      String sortField,
      bool descendingOrder = false,
      List<DataFilter> dataFilters,
      int queryLimit}) {
    _ref = _firestore.collection(collectionPath);

    if (dataFilters != null && dataFilters.length > 0) {
      for (DataFilter dataFilter in dataFilters) {
        _query = _query == null
            ? _ref.where(dataFilter.filterField,
                isEqualTo: dataFilter.isEqualTo,
                isGreaterThan: dataFilter.isGreaterThan,
                isGreaterThanOrEqualTo: dataFilter.isGreaterThanOrEqualTo,
                isLessThan: dataFilter.isLessThan,
                isLessThanOrEqualTo: dataFilter.isLessThanOrEqualTo,
                isNull: dataFilter.isNull,
                arrayContains: dataFilter.arrayContains)
            : _query.where(dataFilter.filterField,
                isEqualTo: dataFilter.isEqualTo,
                isGreaterThan: dataFilter.isGreaterThan,
                isGreaterThanOrEqualTo: dataFilter.isGreaterThanOrEqualTo,
                isLessThan: dataFilter.isLessThan,
                isLessThanOrEqualTo: dataFilter.isLessThanOrEqualTo,
                isNull: dataFilter.isNull,
                arrayContains: dataFilter.arrayContains);
      }
    }
    if (sortField != null) {
      _query != null
          ? _query
              .orderBy(
                sortField,
                descending: descendingOrder,
              )
              .limit(queryLimit)
          : _ref
              .orderBy(sortField, descending: descendingOrder)
              .limit(queryLimit);
    }
    if (queryLimit != null) {
      _query != null ? _query.limit(queryLimit) : _ref.limit(queryLimit);
    }
    _stream = _query != null ? _query.snapshots() : _ref.snapshots();
  }

  AppData.fromExisting(
      {AppData original,
      String sortField,
      bool descendingOrder,
      List<DataFilter> dataFilters})
      : this(
          collectionPath: original.collectionPath,
          sortField: sortField,
          descendingOrder: descendingOrder,
          dataFilters: dataFilters,
        );

  CollectionReference get ref => _ref;

  Query _query;
  CollectionReference _ref;
  Stream<QuerySnapshot> _stream;
  QuerySnapshot _snapshot;

  final String collectionPath;

  Future<List<Map<String, dynamic>>> getDocumentsData() async {
    _snapshot = _query != null
        ? await _query.getDocuments()
        : await _ref.getDocuments();
    try {
      return _snapshot.documents.map((f) {
        return f.data;
      }).toList();
    } catch (e) {
      print('Error fetching data from firestore. Error is ${e.toString()}');
    }
    return null;
  }

  Future<Map<String, dynamic>> getDocument(String documentReferenceId) async {
    try {
      DocumentSnapshot snapshot =
          await _ref.document(documentReferenceId).get();
      return snapshot.data;
    } catch (e) {
      print('Error in returning a single document $e');
    }
    return null;
  }

  Stream<Map<String, Map<String, dynamic>>> getQueryStream(
      {String sortField,
      bool descendingOrder = false,
      List<DataFilter> dataFilters}) {
    if (dataFilters != null && dataFilters.length > 0) {
      for (DataFilter dataFilter in dataFilters) {
        _query = _query == null
            ? _ref.where(dataFilter.filterField,
                isEqualTo: dataFilter.isEqualTo,
                isGreaterThan: dataFilter.isGreaterThan,
                isGreaterThanOrEqualTo: dataFilter.isGreaterThanOrEqualTo,
                isLessThan: dataFilter.isLessThan,
                isLessThanOrEqualTo: dataFilter.isLessThanOrEqualTo,
                isNull: dataFilter.isNull,
                arrayContains: dataFilter.arrayContains)
            : _query.where(dataFilter.filterField,
                isEqualTo: dataFilter.isEqualTo,
                isGreaterThan: dataFilter.isGreaterThan,
                isGreaterThanOrEqualTo: dataFilter.isGreaterThanOrEqualTo,
                isLessThan: dataFilter.isLessThan,
                isLessThanOrEqualTo: dataFilter.isLessThanOrEqualTo,
                isNull: dataFilter.isNull,
                arrayContains: dataFilter.arrayContains);
      }
    }
    if (sortField != null) {
      _query != null
          ? _query.orderBy(
              sortField,
              descending: descendingOrder,
            )
          : _ref.orderBy(sortField, descending: descendingOrder);
    }
    _stream = _query != null ? _query.snapshots() : _ref.snapshots();

    return _stream.map((event) {
      Map<String, Map<String, dynamic>> outputMap =
          new Map<String, Map<String, dynamic>>();
      event.documents
          .forEach((f) => outputMap.putIfAbsent(f.documentID, () => f.data));
      return (outputMap);
    });
  }

  Stream<Map<String, Map<String, dynamic>>> getStream() {
    return _stream.transform(
      StreamTransformer<QuerySnapshot,
          Map<String, Map<String, dynamic>>>.fromHandlers(
        handleDone: (sink) => sink.close(),
        handleData: (event, sink) {
          Map<String, Map<String, dynamic>> outputMap =
              new Map<String, Map<String, dynamic>>();
          event.documents.forEach((f) => outputMap.putIfAbsent(
              f.documentID, () => jsonDecode(jsonEncode(f.data))));
          sink.add(outputMap);
        },
      ),
    );
  }

  Stream<Map<String, dynamic>> getSingleDocumentStream(String documentId) {
    return _ref.document(documentId).snapshots().transform(
          StreamTransformer<DocumentSnapshot,
              Map<String, dynamic>>.fromHandlers(
            handleDone: (sink) => sink.close(),
            handleData: (event, sink) => event.data,
            handleError: (error, stackTrace, sink) {
              print(
                  'error handling single document data stream from firestore: $error');
              sink.add({});
            },
          ),
        );
  }

  Future<void> setData(
      {String documentReferenceId, Map<String, dynamic> data}) {
    return _ref.document(documentReferenceId).setData(data).whenComplete(() {
      print('Document $documentReferenceId modified in ${_ref.id}');
    }).catchError((e) {
      print(e);
    });
  }

  Future<dynamic> deleteData(String documentReferenceId) {
    return _ref.document(documentReferenceId).delete().catchError((e) {
      print(e);
      return false;
    });
  }

  Future<String> addData({Map<String, dynamic> data}) async {
    DocumentReference docRef = await _ref.add(data);
    return docRef.documentID;
  }
}

class DataFilter {
  DataFilter({
    this.filterField,
    this.isEqualTo,
    this.isLessThan,
    this.isLessThanOrEqualTo,
    this.isGreaterThan,
    this.isGreaterThanOrEqualTo,
    this.arrayContains,
    this.isNull,
  });

  final String filterField;
  final dynamic isEqualTo;
  final dynamic isLessThan;
  final dynamic isLessThanOrEqualTo;
  final dynamic isGreaterThan;
  final dynamic isGreaterThanOrEqualTo;
  final dynamic arrayContains;
  final bool isNull;
}

Expected behavior
This code should write to the firestore DB without issue - rolling back to 0.12.9 fixes this bug.

Additional context
There is obviously more to this app than my firestore_service file - if needs be I can provide more code, but the error generating issue is definitely triggered in the addData method. I've run this in debug with breakpoints and can verify that there is data being passed to the _ref.add method.

Metadata

Metadata

Assignees

No one assigned

    Labels

    impact: customerA bug with low impact (e.g. affecting only a few customers or has a workaround). (P3)platform: iosIssues / PRs which are specifically for iOS.plugin: cloud_firestoretype: bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions