mordecai
mordecai

Reputation: 137

Struggling to Update multiple documents at once with flutter cloud Firestore

I have been struggling with this piece of code for a while now, I am developing  a POS app for a shop, so one feature I have is the ability for the app to register every item being added to the cart and when a sale is made, I need those items updated by removing one unit from each items quantity field from my cloud firestore database, so what I did was, every time a user adds an item to the cart, it will add the ID of that document to a string array, like so [4343434343,34343543434] for example, then after the user confirms the sale, I want it to loop through each document and update the field by removing the number 1 from the time quantity field in each item document, I tried simply by creating a for loop with a firebase update inside but had errors, then I tried using batch writes to do it, apparently, it's supposed to help when working with multiple files, the issue is I keep getting the error

Unhandled Exception: Bad state: This batch has already been committed and can no longer be changed

it doesn't even change the value in the update field even once, here is my code, I'm not sure what I'm doing wrong here but I'm going nuts over this. also if you have an alternative to achieve what I'm trying to do it would be much appreciated,

WriteBatch batch = FirebaseFirestore.instance.batch();
//Create a batch
print(cart_numbers.toString());
Future update() async {
    for (var i = 0; i < cart_numbers.length; i++) {
        print('is iterator wooooooorking !!!!!' + i.toString());
        FirebaseFirestore.instance.collection("items")
            .doc(cart_numbers[i].toString()).get().then((value) {
                print('==============================================' +
                    value.data().toString());
                print('==============================================' +
                    value.data()['quantity'].toString());
                int newer = value.data()['quantity'];
                newer--;
                print('newwwwwwwwwwwwwwwwwwwwwwwwerrrrrrrrrrrrr' + newer.toString());
                batch.update(
                    //Give the ref of document.
                    FirebaseFirestore.instance.collection('items')
                    .doc(cart_numbers[i].toString()),
                    //And the data to add in it.
                    {
                        'quantity': newer
                    }
                );
            });
    }

}

void start() async {
    await update();
    batch.commit();
}

start()

here is the debug console

is iterator wooooooorking !!!!!0
I/flutter ( 3091): is iterator wooooooorking !!!!!1
I/flutter ( 3091): is iterator wooooooorking !!!!!2
E/EGL_adreno( 3091): tid 3281: eglSurfaceAttrib(1582): error 0x3009 (EGL_BAD_MATCH)
W/OpenGLRenderer( 3091): Failed to set EGL_SWAP_BEHAVIOR on surface 0x81bff820, error=EGL_BAD_MATCH
I/flutter ( 3091): found data on cloudstore Instance of '_MapStream<QuerySnapshotPlatform, QuerySnapshot>'
I/flutter ( 3091): yessssssssssssssssssssssssssssssssssssssssssssssssssss
I/flutter ( 3091): yessssssssssssssssssssssssssssssssssssssssssssssssssss
I/flutter ( 3091): =============================================={name: bobo2012, quantity: 7, price: 5000, category: Antivirus Software, id: 2467046743, productid: 496184004787577}
I/flutter ( 3091): ==============================================7
I/flutter ( 3091): newwwwwwwwwwwwwwwwwwwwwwwwerrrrrrrrrrrrr6
E/flutter ( 3091): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Bad state: This batch has already been committed and can no longer be change    

the error I keep getting

E/flutter ( 4054): #0      int._throwFormatException (dart:core-patch/integers_patch.dart:131:5)
E/flutter ( 4054): #1      int._parseRadix (dart:core-patch/integers_patch.dart:157:16)
E/flutter ( 4054): #2      int._parse (dart:core-patch/integers_patch.dart:100:12)
E/flutter ( 4054): #3      int.parse (dart:core-patch/integers_patch.dart:63:12)
E/flutter ( 4054): #4      Home.update.<anonymous closure>.<anonymous closure>
package:weepay_pos/home.dart:53
E/flutter ( 4054): #5      List.forEach (dart:core-patch/growable_array.dart:302:8)
E/flutter ( 4054): #6      Home.update.<anonymous closure>
package:weepay_pos/home.dart:52
E/flutter ( 4054): #7      _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter ( 4054): #8      _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 4054): #9      _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter ( 4054): #10     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter ( 4054): #11     Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter ( 4054): #12     Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter ( 4054): #13     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
E/flutter ( 4054): #14     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
E/flutter ( 4054): #15     Query.get (package:cloud_firestore/src/query.dart)
package:cloud_firestore/src/query.dart:1
E/flutter ( 4054): <asynchronous suspension>
E/flutter ( 4054): #16     Home.update
package:weepay_pos/home.dart:51
E/flutter ( 4054): #17     Home.build.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure>
package:weepay_pos/home.dart:1278
E/flutter ( 4054): #18     _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter ( 4054): #19     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 4054): #20     _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter ( 4054): #21     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter ( 4054): #22     Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter ( 4054): #23     Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter ( 4054): #24     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
E/flutter ( 4054): #25     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
E/flutter ( 4054): #26     CollectionReference.add (package:cloud_firestore/src/collection_reference.dart)
package:cloud_firestore/src/collection_reference.dart:1
E/flutter ( 4054): #27     _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter ( 4054): #28     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 4054): #29     _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter ( 4054): #30     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter ( 4054): #31     Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter ( 4054): #32     Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter ( 4054): #33     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
E/flutter ( 4054): #34     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
E/flutter ( 4054): #35     MethodChannelDocumentReference.set (package:cloud_firestore_platform_interface/src/method_channel/method_channel_document_reference.dart)
package:cloud_firestore_platform_interface/…/method_channel/method_channel_document_reference.dart:1
E/flutter ( 4054): #36     _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter ( 4054): #37     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 4054): #38     _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter ( 4054): #39     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
E/flutter ( 4054): #40     Future._propagateToListeners (dart:async/future_impl.dart:725:32)
E/flutter ( 4054): #41     Future._completeWithValue (dart:async/future_impl.dart:529:5)
E/flutter ( 4054): #42     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
E/flutter ( 4054): #43     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
E/flutter ( 4054): #44     MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart)
package:flutter/…/services/platform_channel.dart:1
E/flutter ( 4054): #45     _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter ( 4054): #46     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 4054): #47     _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
E/flutter ( 4054): #48     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)

Upvotes: 1

Views: 2723

Answers (1)

LucasACH
LucasACH

Reputation: 169

There is no need on implementing a for loop. Just get all items and using the forEach() function check if the item is inside the shopping cart. If so, edit the quantity field.

Future update(List<int> cartNumbers) async {
  WriteBatch batch = FirebaseFirestore.instance.batch();

  // 1. Get all items
  // 2. Iterate through each item
  // 3. Parse each document ID
  // 4. Check if cart_numers contains item
  // 5. Change quantity

  FirebaseFirestore.instance.collection("items").get().then((querySnapshot) {
    querySnapshot.docs.forEach((document) {
      try {

        // Only if DocumentID has only numbers
        if (cartNumbers.contains(int.parse(document.id))) {
          batch.update(document.reference,
              {"quantity": document.data()["quantity"] - 1});
        }
      } on FormatException catch (error) {

        // If a document ID is unparsable. Example "lRt931gu83iukSSLwyei" is unparsable.
        print("The document ${error.source} could not be parsed.");
        return null;
      }
    });
    return batch.commit();
  });
}

Try out this example:

class HomePage extends StatelessWidget {

  // In this case, I manually added the items ids
  final List<int> cartNumbers = [121212, 434343, 565656];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Items"),
          actions: [
            Center(
                child: Padding(
              padding: const EdgeInsets.only(right: 15),
              child: Text("Quantity"),
            ))
          ],
        ),
        body: StreamBuilder<QuerySnapshot>(
          stream: FirebaseFirestore.instance.collection("items").snapshots(),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return Column(
                children: [
                  Expanded(
                    child: ListView.builder(
                      itemCount: snapshot.data.docs.length,
                      itemBuilder: (context, index) {
                        return ListTile(
                          title: Text(snapshot.data.docs[index].id),
                          trailing: Text(
                              snapshot.data.docs[index]["quantity"].toString()),
                        );
                      },
                    ),
                  ),
                  RaisedButton(
                    onPressed: () => update(cartNumbers),
                    child: Text("Update"),
                  ),
                ],
              );
            } else {
              return Center(child: CircularProgressIndicator());
            }
          },
        ));
  }
}

Upvotes: 2

Related Questions