Flutter嵌套的StreamBuilders导致错误状态:流已被侦听 [英] Flutter nested StreamBuilders causing Bad state: Stream has already been listened to
问题描述
我正在尝试使用视频 BLoC基本上是具有Sink
输入和Stream
输出的视图模型.在我的示例中,它看起来像这样:
A BLoC is basically a view model with Sink
inputs and Stream
outputs. In my example it looks a bit like this:
class BLoC {
// inputs
Sink<String> inputTextChanges;
Sink<Null> submitButtonClicks;
// outputs
Stream<bool> showLoading;
Stream<bool> submitEnabled;
}
我在层次结构根附近的小部件中定义了BLoC,并将其向下传递到其下的小部件,包括嵌套的StreamBuilders
.像这样:
I have the BLoC defined in a widget near the root of the hierarchy and it is passed down to widgets beneath it, including nested StreamBuilders
. Like so:
顶部的StreamBuilder
在BLoC上侦听showLoading
流,以便可以对其进行重建以显示重叠的进度条.底部的StreamBuilder
侦听submitEnabled
流以启用/禁用按钮.
The top StreamBuilder
listens to a showLoading
stream on the BLoC so that it can rebuild to show an overlaid progress spinner. The bottom StreamBuilder
listens to a submitEnabled
stream to enable/disable a button.
问题是,每当showLoading
流导致顶部StreamBuilder
重建小部件时,它也会同时重建嵌套的小部件.这本身是好的,也是可以预期的.但是,这将导致重新创建底部的StreamBuilder
.发生这种情况时,它将尝试重新订阅BLoC上现有的submitEnabled
流,从而导致Bad state: Stream has already been listened to
The problem is whenever the showLoading
stream causes the top StreamBuilder
to rebuild the widget it rebuilds nested widgets too. This in itself is fine and expected. However this results in the bottom StreamBuilder
being recreated. When this happens it attempts to re-subscribe to the existing submitEnabled
stream on the BLoC causing Bad state: Stream has already been listened to
有什么方法可以在不使所有输出为BroadcastStreams
的情况下完成此操作吗?
Is there any way to accomplish this without making all of the outputs BroadcastStreams
?
(从根本上来说,我还有可能误解了BLoC模式.)
(There is also a chance that I'm fundamentally misunderstanding the BLoC pattern.)
下面的实际代码示例:
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'dart:async';
void main() => runApp(BlocExampleApp());
class BlocExampleApp extends StatefulWidget {
BlocExampleApp({Key key}) : super(key: key);
@override
_BlocExampleAppState createState() => _BlocExampleAppState();
}
class _BlocExampleAppState extends State<BlocExampleApp> {
Bloc bloc = Bloc();
@override
Widget build(BuildContext context) =>
MaterialApp(
home: Scaffold(
appBar: AppBar(elevation: 0.0),
body: new StreamBuilder<bool>(
stream: bloc.showLoading,
builder: (context, snapshot) =>
snapshot.data
? _overlayLoadingWidget(_buildContent(context))
: _buildContent(context)
)
),
);
Widget _buildContent(context) =>
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextField(
onChanged: bloc.inputTextChanges.add,
),
StreamBuilder<bool>(
stream: bloc.submitEnabled,
builder: ((context, snapshot) =>
MaterialButton(
onPressed: snapshot.data ? () => bloc.submitClicks.add(null) : null,
child: Text('Submit'),
)
)
)
]
);
Widget _overlayLoadingWidget(Widget content) =>
Stack(
children: <Widget>[
content,
Container(
color: Colors.black54,
),
Center(child: CircularProgressIndicator()),
],
);
}
class Bloc {
final StreamController<String> _inputTextChanges = StreamController<String>();
final StreamController<Null> _submitClicks = StreamController();
// Inputs
Sink<String> get inputTextChanges => _inputTextChanges.sink;
Sink<Null> get submitClicks => _submitClicks.sink;
// Outputs
Stream<bool> get submitEnabled =>
Observable<String>(_inputTextChanges.stream)
.distinct()
.map(_isInputValid);
Stream<bool> get showLoading => _submitClicks.stream.map((_) => true);
bool _isInputValid(String input) => true;
void dispose() {
_inputTextChanges.close();
_submitClicks.close();
}
}
推荐答案
据我了解BLoC,您应该只有一个连接到StreamBuilder的输出流.此输出流发出一个包含所有必需状态的模型.
As i understand BLoC you should only have one output stream which is connected to a StreamBuilder. This output stream emits a model which contains all required state.
You can see how its done here: https://github.com/ReactiveX/rxdart/blob/master/example/flutter/github_search/lib/github_search_widget.dart
新链接: https://github.com/ReactiveX/rxdart/blob/master/example/flutter/github_search/lib/search_widget.dart
如果需要组合多个流来生成模型(sowLoading和SubmitEnabled),则可以使用RxDart中的Observable.combineLatest
将多个流合并为一个流.我使用这种方法,效果很好.
If you need to combine multiple steams to generate you model (sowLoading and submitEnabled), you can use Observable.combineLatest
from RxDart to merge multiple streams into one stream. I use this approach and it works really nice.
这篇关于Flutter嵌套的StreamBuilders导致错误状态:流已被侦听的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!