Struggling to Update multiple documents at once with flutter cloud Firestore

1,781

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());
            }
          },
        ));
  }
}
Share:
1,781
mordecai
Author by

mordecai

Updated on December 26, 2022

Comments

  • mordecai
    mordecai over 1 year

    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)
    
  • LucasACH
    LucasACH over 3 years
    Share more code please. For me it works fine. Make sure to remove batch.commit() from your start() function. Anyway, just call update() without wrapping it inside a function. Like onTap: () => update()
  • mordecai
    mordecai over 3 years
    cart numbers i included everything involved, what else would you like me to include?, ok. so i should completely remove batch. commit and the start function and it will work?in your code you wrote batch.commit without using the batch function is that possible?
  • LucasACH
    LucasACH over 3 years
    Recently edit my answer. You were right! Needed to add batch.update(). It was only updating the first item. Hope it works now.
  • mordecai
    mordecai over 3 years
    it doesn't work on mine, it doesn't even update once, I added the error to my question a the bottom, I dont know what to do now
  • LucasACH
    LucasACH over 3 years
    At least could you displayed the HomePage from the example below? The items ids are formated as a string or int? Share your database structure aswell
  • mordecai
    mordecai over 3 years
  • mordecai
    mordecai over 3 years
    here my home.dart page where i have commented out all the important parts so you cant miss them, I have also added a photo of my database structure
  • mordecai
    mordecai over 3 years
    everything coudnt fit here so i just put them up on github and by the way, the cart numbers array is an INT, thanks again for the help