4

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.

Delgan
  • 18,571
  • 11
  • 90
  • 141
First_Strike
  • 1,029
  • 1
  • 10
  • 27

1 Answers1

11

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();
  }
}
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Oh my. Your answer is SUPER HELPFUL. Previously I was unaware that the State class has a 'context' property itself. It is only after I tried out your code and find it is actually in Flutter's source code and hidden in Flutter's docs. Thanks! – First_Strike Nov 23 '18 at 13:49
  • @remi do you know if streams work with animation? Working with AnimatedOpacity widget the duration does not working when setting the value with a stream. Only after calling setState but that seems counter to what I have been reading. Is there a proper way to do it? – Claude Jan 06 '19 at 18:23
  • 1
    @Claude They do, but be aware that listening to a stream is asynchronous. So you might see things a frame late. This is usually unnoticeable, but just keep it in mind. – Rémi Rousselet Jan 06 '19 at 21:44
  • I wrote an article about streams ad animation in the context of the bloc pattern: https://medium.com/@ride4sun/flutter-animations-with-the-bloc-pattern-in-6-lines-of-code-53dbb2cba3da – Ride Sun Oct 25 '19 at 19:49
  • what is the ".foo" on Bloc? Shouldn't the listener be in initState? – Ajay Gautam Jun 03 '20 at 22:42