Navigating to a new screen when stream value in BLOC changes Navigating to a new screen when stream value in BLOC changes flutter flutter

Navigating to a new screen when stream value in BLOC changes


You should not use StreamBuilder to handle navigation.StreamBuilder is used to build the content of a screen and nothing else.

Instead, you will have to listen to the stream to trigger side-effects manually. This is done by using a StatefulWidget and overriding initState/dispose as such:

class Example extends StatefulWidget {  final Stream<int> stream;  const Example({Key key, this.stream}) : super(key: key);  @override  ExampleState createState() => ExampleState();}class ExampleState extends State<Example> {  StreamSubscription _streamSubscription;  @override  void initState() {    super.initState();    _listen();  }  @override  void didUpdateWidget(Example oldWidget) {    super.didUpdateWidget(oldWidget);    if (oldWidget.stream != widget.stream) {      _streamSubscription.cancel();      _listen();    }  }  void _listen() {    _streamSubscription = widget.stream.listen((value) {      Navigator.pushNamed(context, '/someRoute/$value');    });  }  @override  void dispose() {    _streamSubscription.cancel();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Container();  }}

Note that if you're using an InheritedWidget to obtain your stream (typically BLoC), you will want to use didChangeDependencies instead of initState/didUpdateWidget.

This leads to:

class Example extends StatefulWidget {  @override  ExampleState createState() => ExampleState();}class ExampleState extends State<Example> {  StreamSubscription _streamSubscription;  Stream _previousStream;  void _listen(Stream<int> stream) {    _streamSubscription = stream.listen((value) {      Navigator.pushNamed(context, '/someRoute/$value');    });  }  @override  void didChangeDependencies() {    super.didChangeDependencies();    final bloc = MyBloc.of(context);    if (bloc.stream != _previousStream) {      _streamSubscription?.cancel();      _previousStream = bloc.stream;      _listen(bloc.stream);    }  }  @override  void dispose() {    _streamSubscription.cancel();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Container();  }}


You can extend StreamBuilder with custom listener like this:

typedef StreamListener<T> = void Function(T value);class StreamListenableBuilder<T> extends StreamBuilder<T> {  final StreamListener<T> listener;  const StreamListenableBuilder({    Key key,    T initialData,    Stream<T> stream,    @required this.listener,    @required AsyncWidgetBuilder<T> builder,  }) : super(key: key, initialData: initialData, stream: stream, builder: builder);  @override  AsyncSnapshot<T> afterData(AsyncSnapshot<T> current, T data) {    listener(data);    return super.afterData(current, data);  }}

Then connect listener for navigation this way:

StreamListenableBuilder(    stream: bloc.streamValue,    listener: (value) {      if (value==1) {        Navigator.push(          context,          MaterialPageRoute(builder: (context) => SomeNewScreen()),        );      }    },    builder: (BuildContext context, AsyncSnapshot<int> snapshot) {      return Container();    });