0

I've got a problem with data which I'm returning from Firestore. I need to get some emails using the Cloud Firestore Plugin:

class _EmailPageState extends State<EmailPage> {
  //StreamBuilder<QuerySnapshot> _emails;

  @override
  void initState() {
    super.initState();
    //_emails = _getEmails();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Refer user'),
      ),
      body: Column(
    children: <Widget>[
      Form(... form code)
      Expanded(child: _getEmails()),
      ],
    )
}

Since _emails is not populated until the data returns from Firestore, I get:

flutter: The following assertion was thrown building Expanded(flex: 1, dirty):
flutter: A build function returned null.
flutter: The offending widget is: Expanded(flex: 1)
flutter: Build functions must never return null. To return an empty space that causes the building widget to
flutter: fill available room, return "new Container()". To return an empty space that takes as little room as
flutter: possible, return "new Container(width: 0.0, height: 0.0)".

I don't understand how to fix the error. The view is displaying correct. But I don't want to have the error.

 StreamBuilder<QuerySnapshot> _getEmails() {
    final query = Firestore.instance
        .collection('emails')
        .where("referer", isEqualTo: uid)
        .snapshots();

    return StreamBuilder<QuerySnapshot>(
      stream: query,
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasError) return Text('Error: ${snapshot.error}');
        switch (snapshot.connectionState) {
          case ConnectionState.waiting:
            return Text('Loading...');
          default:
            return _makeList(snapshot);
        }
      },
    );

    //setState(() {
      //_emails = emailList;
    //});

    //return emailList;
  }

  ListView _makeList(AsyncSnapshot<QuerySnapshot> snapshot) {
    return ListView(
      children: snapshot.data.documents.map((DocumentSnapshot document) {
        return ListTile(
          title: Text(document['email']),
        );
      }).toList(),
    );
  }
Ciprian
  • 3,066
  • 9
  • 62
  • 98

1 Answers1

1

Your _getEmails() is returning null before data arrives. It should return something like CircularProgressIndicator() instead. If you show that function you can get more help, but you can use sample code from here, too: https://docs.flutter.io/flutter/widgets/StreamBuilder-class.html

Edit: ok I see your edit. I guess it does not return null after all. However, Expanded needs to be a child of a Row or a Column. You should either wrap it with one of these, or use Container, Center, etc. to do your positioning.

Also, there is no need to cache the StreamBuilder, you should not cache widgets. You can cache the stream, however. You should at least change your code to be something like

class _EmailPageState extends State<EmailPage> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Refer user'),
      ),
      body: Center(child: _getEmails())
    )
  }
}
Gazihan Alankus
  • 11,256
  • 7
  • 46
  • 57
  • Ok ... maybe I should add more code. There is something I left out. If I don't use `Expanded` the widget doesn't render. Please see my edit. – Ciprian Apr 30 '19 at 13:13
  • Thank you for the tip about caching ... I removed it. – Ciprian Apr 30 '19 at 13:28
  • I don't see where the build function is returning null... You could try this: in your `_makeList` function, see if `snapshot.data.documents.isEmpty` is ever true. Maybe ListView having an empty list is it, who knows. – Gazihan Alankus Apr 30 '19 at 13:32
  • I will. Thank you. You mentioned that I can cache the stream and not the `StreamBuilder`. How would I do that. Can you give me an example? – Ciprian Apr 30 '19 at 13:45
  • 1
    Sure, here is an example of it for `FutureBuilder`: https://stackoverflow.com/a/52249579/679553 He creates the `Future` in `initState` and puts it into a field in the class. Later he uses it in a `FutureBuilder`. This way you are sure that the same `Future` instance will be available to `FutureBuilder` and this will prevent some unwanted rebuilds. You can do the same for `Stream` and `StreamBuilder`. – Gazihan Alankus May 01 '19 at 04:46