使用 didUpdateWidget 替换流导致 "bad state: Stream has already been listened to"

using didUpdateWidget to replace a stream causes "bad state: Stream has already been listened to"

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

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

In initState, subscribe to the object. In didUpdateWidget unsubscribe from the old object and subscribe to the new one if the updated widget configuration requires replacing the object. In dispose, unsubscribe from the object.

每当用户输入新的搜索查询时,我都需要替换 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 并怀疑这就是两个问题(状态不佳和未更新)的来源?我还重新构造了有状态的小部件,而不是仅仅修改它的状态,这有点令人困惑,但小部件旨在作为所有意图和目的的无状态小部件,因此更新它会很痛苦更新状态而不是重建。有什么建议吗?

我没有仔细阅读“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 设置为它(如果它已更改)。

希望遇到类似问题的其他人 运行 能得到帮助!