0
votes

So I'm fairly new in flutter development.

I'm making an app i which the user scans a barcode and searches the firestore database for a document with the scanned code, putting the data found on a list of maps. I've got that figured out.

However, now I need to display the data on a list, for that, I used StreamBuilder. Now, I want it to rebuild the widget whenever the length of the list changes. However I really can't seem to figure out how to actually do it since the "stream" field won't accept the list length as the stream and I can't figure out how to stream the actual length of the list.

Any help would be greatly appreciated, though please try to make it easy to understand since I'm still pretty new to flutter.

Update 1: I probably should have done this from the beginning but anyways here's the code

import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:barcode_scan/barcode_scan.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Firestore firestore = Firestore.instance;
  String barcode = "";
  var barcodeResult;

  List<Map<String, dynamic>> productList;

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
          appBar: new AppBar(
            title: new Text('Barcode Scanner Example'),
          ),
          body: new Center(
            child: new Column(
              children: <Widget>[
                StreamBuilder(
                  initialData: productList.length,
                    stream: ,
                    builder: (context, snapshot) {
                      if (productList.isEmpty) {
                        return Text("You have no packages to deliver");
                      } else {
                        return ListView.builder(itemBuilder: (context, index) {
                            return Column(children: <Widget>[
                            Text(productList[index]["field 1"]),
                            Text(productList[index]["field 2"])
                          ]);
                        });
                      }
                    }),
                new Container(
                  child: new FloatingActionButton(
                      onPressed: scan, child: new Text("Scan")),
                  padding: const EdgeInsets.all(8.0),
                ),
              ],
            ),
          )),
    );
  }

  Future scan() async {
    try {
      String barcode = await BarcodeScanner.scan();
      try {
        barcodeResult = firestore.collection("packages").document(barcode);
        productList.add(
            {"field 1": barcodeResult["field 1"], "field 2": barcodeResult["field 2"]});
      } catch (e) {
        Fluttertoast.showToast(msg: "Object not found in database");
      }
    } on PlatformException catch (e) {
      if (e.code == BarcodeScanner.CameraAccessDenied) {
        setState(() {
          this.barcode = 'The user did not grant the camera permission!';
        });
      } else {
        setState(() => this.barcode = 'Unknown error: $e');
      }
    } on FormatException {
      setState(() => this.barcode =
          'null (User returned using the "back"-button before scanning anything. Result)');
    } catch (e) {
      setState(() => this.barcode = 'Unknown error: $e');
    }
  }
}
1

1 Answers

0
votes

Solution to query data with StreamBuilder

The best way would be to use the Streambuilder listen to the Firebase stream directly. It is both easier to implement and real time.

The steam would them look something like this

stream: Firestore.instance.collection('Fruit')
    .where("code", isEqualTo: scannedBarCode).snapshots(),

It will rebuild anytime a document in the specified collection is updated with the scanned code.

I hope I understood your requirements and you can develop your app further!

Edit to fix OP's problem

After seeing the code, there is no need at all for a StreamBuilder, as you are not using a Stream, you are just querying the database and adding that information to a List

So the solution would be to remove the StreamBuilder and replace it by the code that was inside the builder with very little modifications:

if (productList.isEmpty)
    Text("You have no packages to deliver"),
if (productList.isNotEmpty)
    ListView.builder(
        itemCount: productList.length,
        itemBuilder: (context, index) {
          return Column(
            children: <Widget>[
               Text(productList[index]["field 1"]),
               Text(productList[index]["field 2"])
            ],
          );
        },
     ),

And not forget to initialize the productList to avoid null exceptions (you cannot add data to a List that is null,

List<Map<String, dynamic>> productList = [];

I hope now it works!