Search code examples
dartflutterrxdart

Flutter - BLoC pattern - How to use streams to invoke a method of another widget, i.e. an animation?


Suppose there is a widget with a method controlling visibility animation, toggleVisibility(). In a BLoC pattern, I want to use a stream to invoke that function. I find this pretty tricky.

  1. Since it is an animation rather than a complete redraw, a StreamBuilder doesn't fit.
  2. Manually add a listener to the BLoC streams isn't convenient either.

    1. In initeState() function of the target widget, we don't have a context, so it's hard to get a reference to the BLoC.

    Edit: This is not the case after I read Rémi Rousselet's answer. We can access the context even outside of build() function, because State<T> class has a property named 'context' and is documented in Flutter's docs.... I wasn't aware of that.

    1. In build(context) function of the target widget, we have the context. But the widget can be frequently re-built, so you have to manually clean the outdated subscriptions. Otherwise it will create tons of garbages.
  3. A hack with StreamBuilder can do, since the StreamBuilder has implemented all the subscription and unsubscription functionalities. Insert a StreamBuilder somewhere in the layout of the target widget.
StreamBuilder(
    stream: Bloc.of(context).stream,
    builder: (context, snapshot){
        toggleVisiblity();
        return Container():
    }
);

But this is really a hack. It mixed layout with logic and introduced a useless widget which could cause layout bugs.

So I wonder if there is a good way of doing this in flutter.


Solution

  • You cannot use StreamBuilder to do that. You have to manually listen to the stream

    class Example extends StatefulWidget {
      @override
      ExampleState createState() => ExampleState();
    }
    
    class ExampleState extends State<Example> {
      StreamSubscription subscription;
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        Stream stream = Bloc.of(context).someStream;
        subscription?.cancel();
        subscription = stream.listen((value) {
          // do something
        });
      }
    
      @override
      void dispose() {
        subscription?.cancel();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Container();
      }
    }