#100DaysOfCodeChallenge - Crop Management Information System - Day 10

Recap

On Day 9 we access the device camera and stored the photo in cloud storage.

Overview

In this post, we will discuss retrieving real time data from cloud firestore and displaying it to the user.

Going Deeper

Retrieval and display of data from cloud firestore are divided into three (3) main parts. This division is done for readability and maintainability purposes.

getAllFarmers method

class FarmerService {
  final farmerRef =
      FirebaseFirestore.instance.collection('farmers').withConverter(
            fromFirestore: (snapshot, _) =>
                FarmerServiceModel.fromJson(snapshot.data()!),
            toFirestore: (farmerModel, _) => farmerModel.toJson(),
          );


  Stream<QuerySnapshot<FarmerServiceModel>> getAllFarmers() {
    return farmerRef.snapshots();
  }
}

The getAllFarmers method returns a Stream<QuerySnapshot<FarmerServiceModel>> to AllFarmerCommand().run()

AllFarmerCommand().run()

class AllFarmerCommand extends BaseCommand {
  AllFarmerCommand(BuildContext c) : super(c);

  Stream<QuerySnapshot<FarmerServiceModel>> run() {
    farmerModel.farmers = farmerService.getAllFarmers();
    return farmerModel.farmers;
  }
}

This method calls farmerService.getAllFarmers();, assigns the returned stream to FarmerModel.farmers and to the method the _AllFarmerScreenController()

AllFarmerScreen

class AllFarmerScreen extends StatefulWidget {
  static String routeName = 'AllFarmerScreen';
  const AllFarmerScreen({
    Key? key,
  }) : super(key: key);

  
  _AllFarmerScreenController createState() => _AllFarmerScreenController();
}

This widget is the All Farmers Screen. It is comprised of a controller and layout widget. This widget is responsible for the screen logic and layout respectively.

AllFarmerScreenController

class _AllFarmerScreenController extends State<AllFarmerScreen> {
  late Stream<QuerySnapshot<FarmerServiceModel>> stream;
  
  Widget build(BuildContext context) => _AllFarmerScreenView(this);

  
  void initState() {
    super.initState();
    stream = AllFarmerCommand(context).run();

    // stream = Provider.of<FarmerModel>(context, listen: false).farmers;
  }
}

Stream<QuerySnapshot<FarmerServiceModel>> from farmerService.getAllFarmers(); is stored in a variable called stream when iniState() is called by flutter.

AllFarmerScreenView

class _AllFarmerScreenView
    extends WidgetView<AllFarmerScreen, _AllFarmerScreenController> {
  final state;
  const _AllFarmerScreenView(this.state) : super(state);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('All Farmers'),
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 24.0),
            child: Center(
              child: Text(
                'Farmer Register',
                style: TextStyles.title.bold,
              ),
            ),
          ),
          Expanded(
            child: StreamBuilder<QuerySnapshot<FarmerServiceModel>>(
              stream: state.stream,
              builder: (context, snapshot) {
                if (!snapshot.hasData) {
                  return Center(
                      child: SpinKitDoubleBounce(
                    color: Colours.accentColor(context),
                  ));
                } else if (snapshot.hasError) {
                  SnackBars.errorSnackBar(
                      content: 'Something went wrong', context: context);
                  print('something went wrong');
                  return Text('Something went wrong');
                } else if (snapshot.connectionState ==
                    ConnectionState.waiting) {
                  return Text("Loading");
                } else
                  return ListView(
                    children: snapshot.data!.docs
                        .map((DocumentSnapshot<FarmerServiceModel> document) {
                      FarmerServiceModel farmer = document.data()!;
                      return FarmerIdentificationCard(
                        farmer: farmer,
                      );
                    }).toList(),
                  );
              },
            ),
          )
        ],
      ),
    );
  }
}

This widget is responsible for the layout of the AllFarmerScreen. We will discuss the stream builder part of the code.

Expanded(
            child: StreamBuilder<QuerySnapshot<FarmerServiceModel>>(
              stream: state.stream,
              builder: (context, snapshot) {
                if (!snapshot.hasData) {
                  return Center(
                      child: SpinKitDoubleBounce(
                    color: Colours.accentColor(context),
                  ));
                } else if (snapshot.hasError) {

                  SnackBars.errorSnackBar(
                      content: 'Something went wrong', context: context);
                  print('something went wrong');
                  return Text('Something went wrong');
                } else if (snapshot.connectionState ==
                    ConnectionState.waiting) {
                  return Text("Loading");
                } else
                  return ListView(
                    children: snapshot.data!.docs
                        .map((DocumentSnapshot<FarmerServiceModel> document) {
                      FarmerServiceModel farmer = document.data()!;
                      return FarmerIdentificationCard(
                        farmer: farmer,
                      );
                    }).toList(),
                  );
              },
            ),
          )

Stream builder will receive updates every time a document is added, deleted or edited in the farmers collection.

Before we extract any documents from the snapshot, we do three (3) main checks. First, we check for data in the snapshot, secondly, we check for errors and lastly we check for the connection state. Once we pass the checks, we return a listview.

Within the listview, each document from the snapshot is converted to a FarmerIdentificationCard() widget. Screenshot of the Identification card is shown below.

Wrap Up

In this post, we discussed how to retrieve data from a cloud firestore collection as a Stream<QuerySnapshot. We then used the snapshot to create a Farmer Identification Card widget for each document in the snapshot.

Connect with me

Thank you for reading my post. Feel free to subscribe below to join me on the #100DaysOfCodeChallenge or connect with me on LinkedIn and Twitter. You can also buy me a book to show your support.

18