【问题标题】:using didUpdateWidget to replace a stream causes "bad state: Stream has already been listened to"使用 didUpdateWidget 替换流会导致“状态不佳:流已被收听”
【发布时间】:2021-02-26 11:45:16
【问题描述】:

我有一个 StreamBuilder,我希望能够暂停和取消暂停,并且由于 StreamBuilder doesn't expose it's underlying StreamSubscription,我必须创建一个代理流来转发源流的元素,同时公开一个我可以暂停和取消暂停的 StreamSubscription。这迫使我将无状态小部件转换为有状态小部件,以便我可以访问 dispose 并关闭它的 StreamController。

我正在遵循didUpdateWidget 文档的说明:

在 initState 中,订阅对象。 如果更新的小部件配置需要替换对象,则在 didUpdateWidget 中取消订阅旧对象并订阅新对象。 在 dispose 中,取消订阅该对象。

每当用户输入新的搜索查询时,我都需要替换 Stream。每当接收到新流时,我的有状态小部件都会在 ChangeNotifier 中重新构建。

这是我的有状态小部件的相关部分:

  final Stream<List<Entry>> _entryStream;
  StreamController<List<Entry>> _entryStreamController;
  StreamSubscription<List<Entry>> _entryStreamSubscription;

  _EntryListState(this._entryStream);

  void initStream() {
    _entryStreamController?.close();
    _entryStreamController = StreamController();

    _entryStreamSubscription = _entryStream.listen((event) {
      _entryStreamController.add(event);
    }
    )..onDone(() => _entryStreamController.close());
  }

  @override
  void initState() {
    initStream();
    super.initState();
  }

  @override
  void dispose() {
    _entryStreamController?.close();
    super.dispose();
  }

  @override
  void didUpdateWidget(EntryList oldEntryList) {
    // Initialize the stream only if it has changed
    if (oldEntryList._entryStream != _entryStream) initStream();

    super.didUpdateWidget(oldEntryList);
  }

这是抛出 BadState 错误的行:

    _entryStreamSubscription = _entryStream.listen((event) {
      _entryStreamController.add(event);
    }

这里是构建我的小部件的地方:

              child: Consumer<SearchStringModel>(
                builder: (context, searchStringModel, child) {
                  print('rebuilding with: ${searchStringModel.searchString}');
                  var entryStream = _getEntries(searchStringModel.searchString);
                  return EntryList(entryStream);
                }
              ),

我不认为我特别了解 didUpdateWidget 并怀疑这就是这两个问题(不良状态和未更新)的来源?我还重新构建了有状态小部件,而不是仅仅修改它的状态,这有点令人困惑,但小部件旨在充当所有意图和目的的无状态小部件,因此更新它会很痛苦状态而不是重建。有什么建议吗?

【问题讨论】:

    标签: flutter stream-builder


    【解决方案1】:

    我没有仔细阅读“didUpdateWidget”文档。这是关键段落:

      /// If the parent widget rebuilds and request that this location in the tree
      /// update to display a new widget with the same [runtimeType] and
      /// [Widget.key], the framework will update the [widget] property of this
      /// [State] object to refer to the new widget and then call this method
      /// with the previous widget as an argument.
    

    在我的消费者中,我正在使用新的EntryList 进行重建。如上段所述,框架随后将在 same 状态对象(而不是新状态对象)上调用didUpdateWidget。为了检查配置是否已更改,如果已更改,要访问新配置,我需要访问我的状态对象的 widget 属性。考虑到这一点,这是我对didUpdateWidget 的新实现:

      @override
      void didUpdateWidget(EntryList oldEntryList) {
        super.didUpdateWidget(oldEntryList);
        // Initialize the stream only if it has changed
        if (widget._entryStream != _entryStream) {
          _entryStream = widget._entryStream;
          initStream();
        }
      }
    

    请注意,我现在正在检查widet._entryStream,然后将状态的_entryStream 设置为它是否已更改。

    希望其他遇到类似问题的人会因此得到帮助!

    【讨论】:

      猜你喜欢
      • 2019-07-23
      • 1970-01-01
      • 2019-03-23
      • 2021-12-26
      • 2018-12-26
      • 2021-07-04
      • 2020-03-08
      • 1970-01-01
      • 2021-06-19
      相关资源
      最近更新 更多