0

How do I make sure my batchNo gets updated for every iteration in my for loop? I am confused with the 'right' way of tackling this problem. I am guessing I am supposed to initialise batchNo locally and update it as I go instead of fetching it every time I call loadAndUploadCsvData(). I tried declaring a local _batchNo in my initState() but am unsure on how to access this variable. I tried making a localVariable within my _TestUploadState, but am unsure on how to update the variable within Widget build because it is not a future Build.

What happens: batchNo doesn't get updated each iteration in the for loop, so the data is all stored in a single document. batchNo fetches from firebase (i.e. batchNo = 1), upon the second iteration of the loop batchNo stays 1 and doesn't get updated possibly due to the lack of the await keyword.

Expected: I want batchNo to be updated each iteration in the for loop, so a seperate document is created per iteration. batchNo fetches from firebase (i.e. batchNo = 1), upon the second iteration of the loop batchNo gets updated and batchNo = 2.

class TestUpload extends StatefulWidget {
  const TestUpload({super.key});

  @override
  _TestUploadState createState() => _TestUploadState();
}

class _TestUploadState extends State<TestUpload> {
  Widget build(BuildContext context) {
    
    // batchNo not updating within for loop
    for (BatchData batchData in _batchDataList) {
      loadAndUploadCsvData(batchData.name, batchData.bytes);
    }
  }

  void loadAndUploadCsvData(String name, Uint8List? bytes) async {

    // First batchNo call is not updated on subsequent batchNo
    int batchNo = await getNextBatchNo();

    // Send current batch to firebase
    FirebaseFirestore.instance
        .collection('availableBatches')
        .doc("batch" "$batchNo")
        .set({"batchNo": batchNo});
  }

  // Gets next batch no from firebase from document names
  Future<int> getNextBatchNo() async {
    QuerySnapshot batchCollection = await FirebaseFirestore.instance
        .collection('availableBatches')
        .orderBy('batchNo', descending: true)
        .get();

    // If collection doesn't exist
    if (batchCollection.docs.isEmpty) {
      return 1;
    }

    int batchNo = batchCollection.docs[0].get('batchNo');

    return batchNo + 1;
  }

}

I've tried doing this instead

class TestUpload extends StatefulWidget {
  const TestUpload({super.key});

  @override
  _TestUploadState createState() => _TestUploadState();
}

class _TestUploadState extends State<TestUpload> {
  int _batchNo = 1;

  int iteration = 0;

  final List<BatchData> _batchDataList = [];

  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
          padding: EdgeInsets.all(8),
          child: Column(
            children: [
              const SizedBox(
                height: 12,
              ),
              ElevatedButton(
                  onPressed: () {
                    for (BatchData batchData in _batchDataList) {
                      loadAndUploadCsvData(batchData.name, batchData.bytes);
                      log("iteration: $iteration");
                      iteration++;
                      log("_batchNo: $_batchNo");
                      updateBatchNo();
                    }
                  },
                  child: Text("TESTING")),
            ],
          )),
    );
  }

  void loadAndUploadCsvData(String name, Uint8List? bytes) async {
    String extension = name.split('.').last;

    int batchNo = await getNextBatchNo();

    if (extension == 'xls' || extension == "xlsx") {

      // Do some xls xlsx manipulation
      var excel = Excel.decodeBytes(bytes!);

      for (var table in excel.tables.keys) {
        for (var row in excel.tables[table]!.rows) {
          ContactModel currentContact;

          // Skip first (header) row
          if (row[0]!.colIndex == 0 && row[0]!.rowIndex == 0) {

            continue;
          }
          // Create currentContact Model
          currentContact = ContactModel(
              name: row[0]!.value,
              number: row[1]!.value,
              type: row[2]!.value,
              isCalled: false);

          // Send current batch to firebase
          _firestore
              .collection('availableBatches')
              .doc("batch" "$batchNo")
              .set({"batchNo": batchNo});

          // Send current contact to firebase
          _firestore
              .collection('availableBatches')
              .doc("batch" "$batchNo")
              .collection("batchCollection")
              .add(currentContact.toMap());
        }
      }
    }
  }

  Future<int> getNextBatchNo() async {
    QuerySnapshot batchCollection = await FirebaseFirestore.instance
        .collection('availableBatches')
        .orderBy('batchNo', descending: true)
        .get();

    // If collection doesn't exist
    if (batchCollection.docs.isEmpty) {
      setState(() {
        _batchNo = 1;
      });
      return 1;
    }

    int batchNo = batchCollection.docs[0].get('batchNo');

    setState(() {
      _batchNo = batchNo + 1;
    });

    return batchNo + 1;
  }

  void updateBatchNo() async {
    int batchNo = await getNextBatchNo();
    setState(() {
      _batchNo = batchNo;
    });
  }
}

But still when trying to send my details to firestore through my loop

[log] iteration: 0
[log] _batchNo: 1
[log] iteration: 1
[log] _batchNo: 1
Joey Leo
  • 17
  • 6
  • What are you trying to do here? Why are there no widgets in `build`? – Christopher Moore Jan 31 '23 at 15:14
  • It's not the build part I am having problems with. I am just confused on the correct syntax of updating/fetching my ``batchNo``. My widget build's just currently a button that does the for loop above with an undesired outcome. This is just a summarised version of my actual code, highlighting my problem. – Joey Leo Jan 31 '23 at 15:34
  • Does this answer your question? [What is a Future and how do I use it?](https://stackoverflow.com/questions/63017280/what-is-a-future-and-how-do-i-use-it) – Christopher Moore Jan 31 '23 at 18:23
  • I managed to solve my problem, but not through that thread. I think I overcomplicated and overthought my code when this solved it [Flutter: Wait for async void method](https://stackoverflow.com/questions/59864748/flutter-wait-for-async-void-method). Just had to change my method return type from ``void`` to ``Future`` – Joey Leo Jan 31 '23 at 19:41

1 Answers1

0

Solved the issue by changing my void loadAndUploadCsvData(String name, Uint8List? bytes) return type to Future<void> loadAndUploadCsvData(String name, Uint8List? bytes) and calling await before each iteration

Joey Leo
  • 17
  • 6