【问题标题】:Navigating to a new screen when stream value in BLOC changes当 BLOC 中的流值发生变化时导航到新屏幕
【发布时间】:2019-06-03 17:21:52
【问题描述】:

在 Flutter 中,当流的值发生变化时,我将如何调用 Navigator.push?我尝试了下面的代码,但出现错误。

StreamBuilder(
        stream: bloc.streamValue,
        builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
          if (snapshot.hasData && snapshot.data == 1) {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SomeNewScreen()),
            );
          }

          return Text("");
        });

【问题讨论】:

  • 我们可以知道您遇到了什么错误吗?
  • @JeromeEscalante 我添加了错误

标签: ios mobile dart flutter rxdart


【解决方案1】:

您不应该使用StreamBuilder 来处理导航。 StreamBuilder 用于构建屏幕内容,仅此而已。

相反,您必须收听流以手动触发副作用。这是通过使用StatefulWidget 并覆盖initState/dispose 来完成的:

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();
  }
}

请注意,如果您使用 InheritedWidget 来获取您的流(通常是 BLoC),您将需要使用 didChangeDependencies 而不是 initState/didUpdateWidget

这导致:

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();
  }
}

【讨论】:

  • 我在 Streambuilder 中使用导航逻辑的时间最长。这种方法更有意义,性能提升明显。
  • @BangOperator "didChangeDependencies 被调用(出于某种原因),从而创建了一个新的 bloc" 我认为这是不正确的,使用根据 Rémi 的示例代码继承的小部件,您每次都会得到相同的 bloc 除非 InheritedWidget(它是不可变的)改变自己
  • Navigator.pushNamed(context, '/someRoute/$value'); 的上下文在哪里从哪里来?
【解决方案2】:

您可以像这样使用自定义侦听器扩展StreamBuilder

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);
  }
}

然后以这种方式连接监听器进行导航:

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

【讨论】:

  • 这是更好的答案。将快照暴露给侦听器回调可能有一些好处,但这是一个微不足道的更改。
猜你喜欢
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 2021-12-19
  • 2019-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多