9
votes

I'm writing an app using Flutter and i have to make a transaction using the Firestore.instance.runTransaction(Transaction tx) method. In my Transaction object (or method) i have to update some data using the document reference.

_firestore.runTransaction((Transaction x) async {
  await x.update(Aref, {'data': itemA - y});
  await x.update(Bref, {'data': itemB + y});
})

When the code is running it throw an exception (Here the console log):

E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): Failed to handle method call result E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): java.lang.IllegalStateException: Task is already complete E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at com.google.android.gms.common.internal.Preconditions.checkState(Unknown Source:8) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at com.google.android.gms.tasks.zzu.zzdr(Unknown Source:8) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at com.google.android.gms.tasks.zzu.setResult(Unknown Source:3) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at com.google.android.gms.tasks.TaskCompletionSource.setResult(Unknown Source:2) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$3$1.success(CloudFirestorePlugin.java:283) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at io.flutter.plugin.common.MethodChannel$IncomingResultHandler.reply(MethodChannel.java:169) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at io.flutter.view.FlutterNativeView.handlePlatformMessageResponse(FlutterNativeView.java:187) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at android.os.MessageQueue.nativePollOnce(Native Method) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at android.os.MessageQueue.next(MessageQueue.java:325) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at android.os.Looper.loop(Looper.java:142)

2
Having the same issue. I think that I filed an issue and asked a question here at some point already some time ago, but I cannot seem to find it. - creativecreatorormaybenot
It is a similar issue I encountered, although I am also encountering your issue: stackoverflow.com/questions/51051730/… - creativecreatorormaybenot

2 Answers

1
votes

The error happens when there are multiple call to the firestore api concurrently, you have to nest each funcion inside the "whenComplete(() {}" method of the previous function. This is the wrong code :

g.f.runTransaction((transaction) async {
      DocumentSnapshot snap =
          await transaction.get(/my code);

      await transaction.update(/my code).whenComplete(() {});
    });


g.f.runTransaction((transaction) async {
      DocumentSnapshot freshSnap =
              await transaction.get(/my code));

      await transaction.update(/my code).whenComplete(() {});
});  //here is the problem!! I'have to nest this inside "whenComplete(() {})

this is the error:

E/MethodChannel#plugins.flutter.io/cloud_firestore( 5337): Failed to handle method call result E/MethodChannel#plugins.flutter.io/cloud_firestore( 5337): java.lang.IllegalStateException: Task is already complete

this is right code

g.f.runTransaction((transaction) async {
  DocumentSnapshot snap =
      await transaction.get(/my code);

  await transaction.update(/my code).whenComplete(() {
    g.f.runTransaction((transaction) async {
      DocumentSnapshot freshSnap =
          await transaction.get(/my code);

      await transaction.update(/my code);
    }).whenComplete(() {});
  });
});
0
votes

I had the same problem. Try this. Hope it Helps FIrestore Transaction Handler

Firestore-Transaction-Handler

This is a subroutine to help handle the failing transaction issue when using runTransaction function in cloud_firestore pub for Flutter

The Problem

This code is a solution to errors with cloud_firestore package for Flutter for the following errors 1. PlatformException(Error performing Transaction#get, Transaction has already completed., null) 2. DoTransaction failed: Document version changed between two reads.

From my experience, I realized that the issue occurs when I try to update a record which was only just created. For some reason the tx.get() (see the example for runTransaction in https://pub.dev/packages/cloud_firestore) is unable to get the record that was just created and the update operation fails. I found that if we wait for a bit and try again, we will be able to get the record and update it. To make things easy, I have created a function that does the update and run transaction for you.

The Solution

Here is an example :

     await fcmTransactionHandler(
      postRef: postRef, //  This is your DocumentReference that you want to update
      key: 'myfield', // This is the field that you want to update
      validationFunction: updateMyField, // This is a function that allows you to check 
      // some condition in the record you 'get' before updating
 );

validationFunction takes a dynamic value (value of the field you want to update) as input and gives the {} value that you will update in the record. For example, if I want to update a field myfield if it is say true, then I will create a function updateMyField as follows

Map<String, dynamic> updateMyField({dynamic value}) {
    return  value ? <String, dynamic>{'myfield': true} : <String, dynamic>{'myfield': value};
  }

Then this function is passed to validationFunction. Why is it designed like this ? As you have seen, we are trying to get the record after a while and then update it. What if the record has been updated by someone else in between ? In that case, we have to validate the data we get when the record is available for us, to prevent any incorrect updates. validationFunction helps us do that.