0

From this answer:

The build method is designed in such a way that it should be pure/without side effects.

and

This means that the build method should not trigger an http call or modify any state.

But this contradicts with firestore plugin usage example(condensed for briefness):

class BookList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: Firestore.instance.collection('books').snapshots(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
      //  do something with books
      },
    );
  }
}

Anytime when build method called, builder function from StreamBuilder called as well.

What I tried:

...
stream: Firestore.instance.collection('books').snapshots().distinct(),
...

Neither advice from previously mentioned answer works for this case.

D31
  • 112
  • 1
  • 10
  • 2
    The fact that `builder` is called again has nothing to do with the purity of the function though ¬ and it is totally normal – Rémi Rousselet Mar 11 '19 at 20:53
  • @Rémi Rousselet, but then it will fetch data again, this time needlessly, which is bad, isn't it? – D31 Mar 11 '19 at 21:27
  • 1
    No it won't. The builder is called again, but the data isn't fetched again – Rémi Rousselet Mar 11 '19 at 21:28
  • @Rémi Rousselet, it's cool, so if `builder` can be called repeatedly without problems, it is differs to use `StatefulWidget` approach instead of plain `StatelessWidget`? – D31 Mar 11 '19 at 21:50
  • 1
    Yes because with the `StatelessWidget`, opening the keyboard for example, will make your build method create a new stream. Which may cause extraneous firebase request (although it's hard to verify). – Rémi Rousselet Mar 11 '19 at 21:55

1 Answers1

2

The solution is the same actually: Make a StatefulWidget

class Foo extends StatefulWidget {
  @override
  _FooState createState() => _FooState();
}

class _FooState extends State<Foo> {
  Stream<QuerySnapshot> stream;

  @override
  void initState() {
    super.initState();
    stream = Firestore.instance.collection('books').snapshots();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: stream,
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        //  do something with books
      },
    );
  }
}
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • thanks for answer, but I added prints in `build` and `builder` and sadly they both printed anytime after, for example, I changed phone orientation – D31 Mar 11 '19 at 21:13
  • 1
    @D31 But as said in the post from the answer you linked (which is mine btw :D): It doesn't matter. `build` (and therefore `builder`) can be called any time. – Rémi Rousselet Mar 11 '19 at 21:21
  • 1
    In fact, precisely because the `builder` is triggered so regularly is why it should be side-effect free. The actual work of getting the data happens in your `stream` property, which doesn't get triggered for every `build`. – Frank van Puffelen Mar 11 '19 at 21:24