Search code examples
flutterasynchronousgoogle-cloud-firestorebuilder

Flutter: How to update widgets when their content arrives from Google Firestore?


OK so my mission is, 1. download a Record from Firestore, 2. process this into state variables 3. inject data into widgets.

I've tried doing this by downloading stuff in initState, I made it Async, however the build method was then called before Firestore download completed, I didn't have the information ready for the widgets and it crashed.

I read that when a Widget is expected to change after being laid out, that I should use a Builder wrapper for the widgets. Which brought me to this:

      Widget build(BuildContext context) {
          return Scaffold(
            appBar: AppBar(
                title: Text(
                    "Job details")), // TODO might have to modify to accomodate edits
            body: _buildBody(context, widget.job.country, widget.job.area, widget.job.jobDetailedId, widget.job),
          );
}

So I'm calling buildBody with context for a Builder, seemingly I lose access to my this.job class object (why?!) so I pass some crucial parameters in for getting the Firestore record.

The aim of _buildBody is to download the firestore record, without using ASYNC await. But it never executes any of the code beyond

.then((jobrecord {

Widget _buildBody(BuildContext context, String countryCode, String area, String jobDetailedId) {
  Job detailedJob;
  Firestore.instance.collection("$countryCode/$area/JobsDetailed").document(jobDetailedId).get().then((jobRecord) {
  return Builder(builder: (BuildContext context) {
      if(jobRecord == null) {
        return Text("Document doesn't exist");
      }
      else {
        detailedJob = Job.fromSnapshot(jobRecord);
        detailedJob.jobDetailedId = jobRecord.documentID;
        return _screenBuild(context, detailedJob);
      }
  });
  });
}

Widget _screenBuild(BuildContext context, Job detailedJob) {
  return Text(detailedJob.description);
}

I thought .then would fire when Firestore returned the document, it doesn't fire.

Is this the best, most clear way of attacking the problem? Would it be better to use a Future? Or Asnc await from initState(), and then use setState()?


Solution

  • This snippet should do the work.

    Widget _buildBody(BuildContext context, String countryCode, String area, String jobDetailedId) {
      Job detailedJob;
        return StreamBuilder<DocumentSnapshot>(
              stream: Firestore.instance.collection("$countryCode/$area/JobsDetailed").document(jobDetailedId).snapshots(),
              builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return Center(
                    child: CircularProgressIndicator(),
                  );
                } else {
                  var jobRecord = snapshot.data.data; // contains document fields
                  if(jobRecord == null) {
                    return Text("Document doesn't exist");
                  }
                  else {
                    detailedJob = Job.fromSnapshot(snapshot.data);
                    detailedJob.jobDetailedId = jobRecord.documentID;
                    return _screenBuild(context, detailedJob);
                  }
                }
              });
    }