Navigating to a new screen when stream value in BLOC changes

前端 未结 2 916
不思量自难忘°
不思量自难忘° 2020-12-01 01:44

In Flutter how would I call Navigator.push when the value of a stream changes? I have tried the code below but get an error.

StreamBuilder(
        stream: b         


        
相关标签:
2条回答
  • 2020-12-01 02:07

    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();
      }
    }
    
    0 讨论(0)
  • 2020-12-01 02:31

    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();
        });
    
    0 讨论(0)
提交回复
热议问题