Avoid repetition in BLoCs and RxDart - dart

hopefully I can make myself clear.
After video and tutorials, I found this way to have some widgets to input data to the bloc (valueSetting) and some others to get this data (value).
What I am asking is if there is a better way (there has to be..). I want to avoid the need to have 4 variables for just 1 real value shared between widgets.
import 'dart:async';
import 'package:rxdart/subjects.dart';
class BlocExample {
final _valueSettingController = StreamController<bool>();
// object use by widget to push data
Sink<bool> get valueSetting => _valueSettingController.sink;
final _value = BehaviorSubject<bool>(seedValue: false);
// object used by widget to get data
Stream<bool> get value => _value.stream;
BlocExample() {
_valueSettingController.stream.listen(_value.add);
}
void dispose() {
_value.close();
_valueSettingController.close();
}
}

First of, let me say that you can remove the private variables by using a custom factory constructor. Here's an example:
class MyBloc {
final Sink<bool> input;
final Stream<bool> output;
final VoidCallback _dispose;
MyBloc._({this.input, this.output, VoidCallback dispose}) : _dispose = dispose;
factory MyBloc() {
final mainController = BehaviorSubject(seedValue: false);
return MyBloc._(
input: mainController.sink,
output: mainController.stream,
dispose: () {
mainController.close();
},
);
}
void dispose() {
_dispose();
}
}
Secondly, the problem you're trying to solve is actually not a problem. While it seems at first that there's a lot of duplicates; in reality they serve different purposes.
In many situations, your Stream will be more than just _controller.stream. For example, for whatever reason you may want to transform the value before exposing it:
final mainController = BehaviorSubject(seedValue: false);
final Stream<bool> output = mainController.map((foo) => !foo);
This code makes that the output stream reverses the value of everything passed to mainController.sink
But in my situation this is not the case. So why 3 variables that point to the same thing?
The fact that in your situation, your controller is both the sink and stream without transformation is an implementation detail and may be subject to changes.
By exposing Sink/Stream as done before, you actually abstract this implementation detail. So that in the future if your stream needs custom operations; no change will be required by your UI.
This is not necessary. But recommended.

You can do something like this :)
enum STREAM_GROUP {
TYPE1,TYPE2,TYPE3
}
class BlocExample {
Map<STREAM_GROUP, StreamController<bool>> groups = new Map();
Stream<bool> getValue(STREAM_GROUP type){
return groups[type].stream;
}
Sink<bool> getValueSetting(STREAM_GROUP type){
return groups[type].sink;
}
BlocExample() {
groups[STREAM_GROUP.TYPE1] = StreamController<bool>();
groups[STREAM_GROUP.TYPE2] = StreamController<bool>();
groups[STREAM_GROUP.TYPE3] = StreamController<bool>();
groups.forEach((groupType, streamController){
final currentValue = BehaviorSubject<bool>(seedValue: false);
streamController.stream.listen(currentValue.add);
});
}
void dispose() {
groups.forEach((groupType, streamController){
streamController.close();
});
}
}

Related

Is it possible to add an item to a List contained in a Stream in Dart?

Problem
I have a Stream<List> which is being listened to in several classes and I need all the classes to receive the updated stream once a value is added to the list in the Stream.
What I have tried
void main() {
StreamedList<String> dataStream = StreamedList();
dataStream.data.listen((list) => print(list));
dataStream.updateList(['Apple', 'Orange']);
dataStream.addToList('Mango'); // This is what I want to do
}
This is the code for StreamList class
class StreamedList<T> {
StreamController<List<T>> _controller = StreamController.broadcast();
Stream<List<T>> get data => _controller.stream;
void updateList(List<T> list) {
_controller.sink.add(list);
}
void addToList(T value) {
// Is it possible to do this?
// List<T> dataList = await _controller.data;
// dataList.add(value);
// updateList(dataList);
}
void dispose() {
_controller.close();
}
}
I have tried different APIs from the dart:async library including Stream.first, etc. which return a Future<List<T>>. But the problem is that this Future resolves only after something is added to the Stream later (eg. by calling the StreamedList.updateList function).
Question
How do I add a single value to the List inside the Stream?
You are misunderstanding what a Stream does. It doesn't "contain" data. It merely accepts data on one end (the sink) and propagates it out the other end (the stream), and any listeners gain access to the streamed object. Trying to "insert an item in a list within the stream" doesn't make any conceptual sense.
If you want to push a list with an additional item, take the old list, append the item to it, then re-add the list to the stream.
class StreamedList<T> {
StreamController<List<T>> _controller = StreamController.broadcast();
Stream<List<T>> get data => _controller.stream;
List<T> _list = [];
void updateList(List<T> list) {
_list = list;
_dispatch();
}
void addToList(T value) {
_list.add(value);
_dispatch();
}
void _dispatch() {
controller.sink.add(_list);
}
void dispose() {
_list = null;
_controller.close();
}
}
If you wanted to be doubly safe, you could recreate the list after every addToList, since if a listener captured the list elsewhere and modified its contents, that would affect _list as well.
void addToList(T value) {
_list = [..._list, value];
_dispatch();
}

What's the equivalent to this[x] in Dart?

For instance, in Javascript I can do something like:
class Foo {
x = 'baz';
bar() {
const someVar = 'x';
console.log(this[someVar]);
// Output: 'baz';
}
}
Hopefully that's relatively clear - it boils down to accessing a member variable by another variable's contents. How is this achieved in Dart?
This is not trivial in Dart. Dart doesn't have a syntax to access class properties with [].
There are a couple of approaches though:
Mirrors:
https://api.dartlang.org/stable/2.6.1/dart-mirrors/dart-mirrors-library.html
Basically you have access to everything and offers the biggest freedom. You can check what properties a class has, access them via names and so on. Big disadvantage is that the generated JS (if targeting web) will be huge. Flutter doesn't support it at all.
Reflectable
To deal with the large generated JS, you can use package:reflectable. Never tried it with Flutter. It's a bit more to set up and start using bit it works.
Dart only solution 1
You can overload [] operator on a class:
class Foo {
final _backing = <String, String>{
'foo': 'bar'
};
operator [](String val) {
return _backing[val];
}
}
void main() {
final inst = Foo();
print(inst['foo']);
}
Dart only solution 2
Just use a map :) Well sort of... If you are dealing with complex types and you want to add some extra functionality to your map, you can do something like this:
import 'dart:collection';
class StringMap extends Object with MapMixin<String, String> {
final _backing = <String, String>{};
#override
String operator [](Object key) {
return _backing[key];
}
#override
void operator []=(String key, String value) {
_backing[key] = value;
}
#override
void clear() {
_backing.clear();
}
#override
Iterable<String> get keys => _backing.keys;
#override
String remove(Object key) {
return _backing.remove(key);
}
}

Connecting a sink of BLoC with another BLoC

I am using the BLoC pattern as described at the Google IO talk.
I have a simple BLoC which is used to display a alert in the UI whenever a string is added to messageSink:
class AlertBloc {
final _message = BehaviorSubject<String>();
AlertBloc() {}
Stream<String> get message => _message.stream;
Sink<String> get messageSink => _message.sink;
void dispose() {
_message.close(); }
}
Elsewhere in the app, I have another BLoC which needs to add a string to messageSink, when a certain condition is met.
I noticed it is not a good idea to provide the whole BLoC from the Google I/O repo for the talk, and they provide advice for connecting a stream from a BLoC to another BLoC sink:
Note that we are not providing [CartBloc] to the
[ProductSquareBloc] directly, although it would be easier to
implement. BLoCs should not depend on other BLoCs (separation of
concerns). They can only communicate with each other using
streams. In this case, the [CartBloc.items] output plugs into the
[ProductSquareBloc.cartItems] input.
My question is how to connect a sink from a BLoC to another BLoC stream?
Here is a simple example for you. Imagine the following two BLoCs:
The first one exposes a Stream and populates it with some values:
class ProducerBLoC {
//Controller is private - you do not want to expose it
final StreamController<int> _productionController = StreamController<int>();
//Instead, you expose a stream
Stream<int> get production => _productionController.stream;
//This method generates some values and puts them to stream
void produceValue() {
_productionController.sink.add(1);
_productionController.sink.add(2);
_productionController.sink.add(3);
}
//Don't forget to close your controllers
void dispose() {
_productionController.close();
}
}
The other one exposes a Sink and processes values that are put into it.
class ConsumerBLoC {
//Controller is private - you do not want to expose it
final StreamController<int> _consumptionController = StreamController<int>();
//Instead, you expose a sink
StreamSink<int> get consumption => _consumptionController.sink;
//In class constructor we start listening to the stream of values
ConsumerBLoC() {
_consumptionController.listen((value) {_consumeValue(value);} );
//or simply: _consumptionController.listen(_consumeValue); //theese are the same
}
//This method generates some values and puts them to stream
void consumeValue(int value) {
//Do something with the value
print('Value processed: $value');
}
//Don't forget to close your controllers
void dispose() {
_consumptionController.close();
}
}
Now, the task is to connect production stream to consumption sink. As you have correctly noticed, you do not want for any of two BLoCs to know anything about existence of the other one. So none of the two should hold references to the other one or even create instances of another one. Instead, you connect them using your Widget class:
//Define some widget to represent main screen of your application
class MainScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MainScreenState();
}
//And define a state for this widget (state does not need to be public)
class _MainScreenState extends State<MainScreen> {
//You define both blocks here
ProducerBLoC _producer = new ProducerBLoC();
ConsumerBLoC _consumer = new ConsumerBLoC();
//Now, either do it in _MainScreenState constructor, or in the initState() method
#override
void initState() {
super.initState();
//Connect production stream with consumption sink
_producer.production.listen((value) => _consumer.consumption.add(value));
//Or, beautifully: _producer.production.pipe(_consumer.consumption);
}
#override
Widget build(BuildContext context) {
//The exact implementation does not matter in current context
}
//And don't forget to close your controllers
#override
dispose() {
super.dispose();
_producer.dispose();
_consumer.dispose();
}
}
This way, any value generated by ProducerBLoC will immediately be consumed by ConsumerBLoC. And, what's the most important, - both BLoCs are completely independent from one another!
The exact same way as you'd do with streams: Passing it as parameter
class Bloc {
final Sink<int> _external;
Bloc(this._external);
}

What are the pros and cons of async, when to and when not to use it and what other alternatives to callback are there?

callbacks or asynchronous methods or other options
A solution to the callback plague is "await" and "async" or more specifacally 'dart:async' library.
Now, what is the cost of asynchrony?
When should we not use them?
What are the other alternatives?
The below is a badly coded non-polymer custom element that acts like a messageBox in desktop environment. It gives me less braces and parenthesis-es but requires the caller to be also async or use "show().then((v){print(v);});" pattern. Should I avoid the pattern like this?
Is callback better? Or there is an even smarter way?
Polling version
import 'dart:html';
import 'dart:async';
void init(){
document.registerElement('list-modal',ListModal);
}
class ListModal extends HtmlElement{
ListModal.created():super.created();
String _modal_returns="";
void set modal_returns(String v){
///use the modal_returns setter to
///implement a custom behaviour for
///the return value of the show method
///within the callback you can pass on calling append .
_modal_returns=v;
}
factory ListModal(){
var e = new Element.tag('list-modal');
e.style..backgroundColor="olive"
..position="absolute"
..margin="auto"
..top="50%"
..verticalAlign="middle";
var close_b = new DivElement();
close_b.text = "X";
close_b.style..right="0"
..top="0"
..margin="0"
..verticalAlign="none"
..backgroundColor="blue"
..position="absolute";
close_b.onClick.listen((_){
e.hide();
});
e.append(close_b,(_)=>e.hide());
e.hide();
return e;
}
#override
ListModal append(
HtmlElement e,
[Function clickHandler=null]
){
super.append(e);
if(clickHandler!=null) {
e.onClick.listen(clickHandler);
}else{
e.onClick.listen((_){
this.hide();
_modal_returns = e.text;
});
}
return this;
}
Future<String> show() async{
_modal_returns = '';
this.hidden=false;
await wait_for_input();
print(_modal_returns);
return _modal_returns;
}
wait_for_input() async{
while(_modal_returns=="" && !this.hidden){
await delay();
}
}
void hide(){
this.hidden=true;
}
Future delay() async{
return new Future.delayed(
new Duration(milliseconds: 100));
}
}
Non-polling version
In response to Günter Zöchbauer's wisdom(avoid polling), posting a version that uses a completer. Thanks you as always Günter Zöchbauer:
import 'dart:html';
import 'dart:async';
void init(){
document.registerElement('list-modal',ListModal);
}
class ListModal extends HtmlElement{
ListModal.created():super.created();
String _modal_returns="";
Completer _completer;
void set modal_returns(String v){
///use the modal_returns setter to
///implement a custom behaviour for
///the return value of the show method.
///Use this setter within the callback for
///append. Always call hide() after
///setting modal_returns.
_modal_returns=v;
}
factory ListModal(){
var e = new Element.tag('list-modal');
e.style..backgroundColor="olive"
..position="absolute"
..margin="auto"
..top="50%"
..verticalAlign="middle";
var close_b = new DivElement();
close_b.text = "X";
close_b.style..right="0"
..top="0"
..margin="0"
..verticalAlign="none"
..backgroundColor="blue"
..position="absolute";
close_b.onClick.listen((_){
e.hide();
});
e.append(close_b,(_){e.hide();});
e.hide();
return e;
}
#override
ListModal append(
HtmlElement e,
[Function clickHandler=null]
){
super.append(e);
if(clickHandler!=null) {
e.onClick.listen(clickHandler);
}else{
e.onClick.listen((_){
_modal_returns = e.text;
this.hide();
});
}
return this;
}
Future<String> show() async{
_modal_returns = '';
_completer = new Completer();
this.hidden=false;
return _completer.future;
}
void hide(){
hidden=true;
_completer?.complete(_modal_returns);
_completer=null;
}
}
Usually there is no question whether async should be used or not. Usually one would try to avoid it. As soon as you call an async API your code goes async without a possibility to choose if you want that or not.
There are situations where async execution is intentionally made async. For example to split up large computation in smaller chunks to not starve the event queue from being processed.
On the server side there are several API functions that allow to choose between sync and async versions. There was an extensive discussion about when to use which. I'll look it up and add the link.
The disadvantages of using async / await instead of .then() should be minimal.
minimal Dart SDK version with async / await support is 1.9.1
the VM needs to do some additional rewriting before the code is executed the first time, but this is usually neglectable.
Your code seems to do polling.
wait_for_input() async {
while(_modal_returns=="" && !this.hidden){
await delay();
}
}
This should be avoided if possible.
It would be better to let the modal manage its hidden state itself (by adding a hide() method for example), then it doesn't have to poll whether it was hidden from the outside.

How to create a StreamTransformer in Dart?

Trying to build a custom StreamTransformer class, however a lot of the examples out there seem to be out of date, and the one found in the documentation isn't (what some typed languages might consider anyway) as a class (found here: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:async.StreamTransformer). This doesn't seem like a very Dart-like way of approaching it and rather more of a Javascript-like way (which I'm using Dart to avoid).
Many online sources say this is how you create a StreamTransformer, however there errors when extending it.
class exampleStreamTransformer extends StreamTransformer
{
//... (This won't work)
}
'Implements' seems to be the way to go, along with implementing the bind function needed:
class exampleStreamTransformer implements StreamTransformer
{
Stream bind(Stream stream)
{
//... (Go on to return new stream, etc)
}
}
I can't seem to find any examples of this way, but have thrown something together myself (which is accepted in my IDE, but isn't accepted at runtime, I get a null object error when it tries to use pause getter):
class exampleStreamTransformer implements StreamTransformer
{
StreamController<String> _controller;
StreamSubscription<String> _subscription;
Stream bind(Stream stream)
{
_controller = new StreamController<String>(
onListen: ()
{
_subscription = stream.listen((data)
{
// Transform the data.
_controller.add(data);
},
onError: _controller.addError,
onDone: _controller.close,
cancelOnError: true); // Unsure how I'd pass this in?????
},
onPause: _subscription.pause,
onResume: _subscription.resume,
onCancel: _subscription.cancel,
sync: true
);
return _controller.stream;
}
}
Would like to achieve it this way, as in the 'typed' way of producing the class, any help is much appreciated, thank you.
Why don't you use StreamTransformer.fromHandler():
import 'dart:async';
void handleData(data, EventSink sink) {
sink.add(data*2);
}
void main() {
StreamTransformer doubleTransformer = new StreamTransformer.fromHandlers(handleData: handleData);
StreamController controller = new StreamController();
controller.stream.transform(doubleTransformer).listen((data) {
print('data: $data');
});
controller.add(1);
controller.add(2);
controller.add(3);
}
Output:
data: 2
data: 4
data: 6
Okay. Here's another working example:
import 'dart:async';
class DuplicateTransformer<S, T> implements StreamTransformer<S, T> {
StreamController _controller;
StreamSubscription _subscription;
bool cancelOnError;
// Original Stream
Stream<S> _stream;
DuplicateTransformer({bool sync: false, this.cancelOnError}) {
_controller = new StreamController<T>(onListen: _onListen, onCancel: _onCancel, onPause: () {
_subscription.pause();
}, onResume: () {
_subscription.resume();
}, sync: sync);
}
DuplicateTransformer.broadcast({bool sync: false, bool this.cancelOnError}) {
_controller = new StreamController<T>.broadcast(onListen: _onListen, onCancel: _onCancel, sync: sync);
}
void _onListen() {
_subscription = _stream.listen(onData,
onError: _controller.addError,
onDone: _controller.close,
cancelOnError: cancelOnError);
}
void _onCancel() {
_subscription.cancel();
_subscription = null;
}
/**
* Transformation
*/
void onData(S data) {
_controller.add(data);
_controller.add(data); /* DUPLICATE EXAMPLE!! REMOVE FOR YOUR OWN IMPLEMENTATION!! */
}
/**
* Bind
*/
Stream<T> bind(Stream<S> stream) {
this._stream = stream;
return _controller.stream;
}
}
void main() {
// Create StreamController
StreamController controller = new StreamController.broadcast();
// Transform
Stream s = controller.stream.transform(new DuplicateTransformer.broadcast());
s.listen((data) {
print('data: $data');
}).cancel();
s.listen((data) {
print('data2: $data');
}).cancel();
s.listen((data) {
print('data3: $data');
});
// Simulate data
controller.add(1);
controller.add(2);
controller.add(3);
}
Let me add some notes:
Using implements seems to be the right way here when looking at the source code of other dart internal transformers.
I implemented both versions for regular and a broadcast stream.
In case of a regular stream you can call cancel/pause/resumt directly on the new stream controller because we can only listen once.
If you use a broadcast stream I found out that listen() is only called if there is no one listening already to the stream. onCancel behaves the same. If the last subscriber cancels its subscription, then onCancel is called. That's why it is safe to use the same functions here.
Unlike map, transformers are more powerful and allows you to maintain an internal state, and emit a value whenever you want. It can achieve things map can't do, such as delaying, duplicating values, selectively omitting some values, and etc.
Essentially, the implementation requires a bind method that provides a new stream based on an old stream being passed in, and a cast method that helps with type-checking during run-time.
Here's an over-simplified example of implementing a "TallyTransformer" that transforms a stream of integer values into a stream of sums. For example, if the input stream so far had 1, 1, 1, -2, 0, ..., the output stream would've been 1, 2, 3, 1, 1, ..., i.e. summing all inputs up to this point.
Example usage: stream.transform(TallyTransformer())
class TallyTransformer implements StreamTransformer {
StreamController _controller = StreamController();
int _sum = 0; // sum of all values so far
#override
Stream bind(Stream stream) {
// start listening on input stream
stream.listen((value) {
_sum += value; // add the new value to sum
_controller.add(_sum); // emit current sum to our listener
});
// return an output stream for our listener
return _controller.stream;
}
#override
StreamTransformer<RS, RT> cast<RS, RT>() {
return StreamTransformer.castFrom(this);
}
}
This example is over-simplified (but still works) and does not cover cases such as stream pausing, resuming or canceling. If you run into "Stream has already been listened" error, make sure streams are broadcasting.
https://github.com/dart-lang/sdk/issues/27740#issuecomment-258073139
You can use StreamTransformer.fromHandlers to easily create
transformers that just convert input events to output events.
Example:
new StreamTransformer.fromHandlers(handleData: (String event, EventSink output) {
if (event.startsWith('data:')) {
output.add(JSON.decode(event.substring('data:'.length)));
} else if (event.isNotEmpty) {
output.addError('Unexpected data from CloudBit stream: "$event"');
}
});
If you want to simply transform values using a function like this
int handleData(int data) {
return data * 2;
}
use map method of Stream
stream
.map(handleData)
.listen((data) {
print('data: $data');
});
Full example:
import 'dart:async';
int handleData(int data) {
return data * 2;
}
void main() {
final controller = StreamController<int>();
controller.stream
.map(handleData)
.listen((data) {
print('data: $data');
});
controller.add(1);
controller.add(2);
controller.add(3);
}
See more examples on dart.dev

Resources