Why is the Streamlistener reacting differently? - flutter

I'm trying to use the Streambuilder in cooperation with Bloc. The problem is that somehow the UI updates only when the expensive Funktions are finished. It seems that then, and only then updates are made to the stream. But I can not figure out why?
I Implemented a simple Bloc that has 4 events:
1. a Future called over the Bloc Sate Object
2. a Future called inside the Bloc
3. a Funktion called inside the Bloc
4 just using Future.delay
I'm Trying to understand why everything behaves as expectetd, but not when I use first two. My guess is I have a basic misunderstanding of the eventloop but I can not see why there should be a difference in behavior between 2 and 4 or 1 and 4
To make it easy I made an example Project on Github
https://github.com/pekretsc/bloceventloop.git
So I have my refresh methode that ads the new state to the Stream.
if (event is ExpensiveEventInState) {
refresh(BlocUIState.Waiting);
String result = await blocState.doSomeThing();
if (result == '') {
refresh(BlocUIState.Fin);
} else {
refresh(BlocUIState.Fail);
}
}
if (event is ExpensiveEventWhyDoesThisWork) {
refresh(BlocUIState.Waiting);
await Future.delayed(Duration(seconds: 3));
refresh(BlocUIState.Fin);
}
so the question is, should the first and second event not behave the same way?
What happens though is that in the first case the refresh is ignored completely and just the Fin is added to the stream. (I checked that, its not that it is too fast to recognize)

StreamListener's callback is called immediately when an event is pushed on the stream.
On the other hand, StreamBuilder's builder callback isn't. For most common use-cases, it is called at most once per frame.

Related

flutter_bloc 8: what is the best practice for listening to state changes from another bloc

What is the best practice for listening to another bloc’s state changes?
This answer was relevant in previous version, but it doesn’t work in version 8 (.listen method doesn’t exist anymore on a bloc): https://stackoverflow.com/a/62785980/160919
FilteredTodosBloc({#required this.todosBloc}) {
todosSubscription = todosBloc.listen((state) {
if (state is TodosLoadSuccess) {
add(TodosUpdated((todosBloc.state as TodosLoadSuccess).todos));
}
});}
What is the recommended approach to listen to a state change from another bloc in flutter_bloc 8?
State stream is now exposed via stream getter, so you can still use almost the same code:
FilteredTodosBloc({required this.todosBloc}) {
todosSubscription = todosBloc.stream.listen((state) {
if (state is TodosLoadSuccess) {
add(TodosUpdated((todosBloc.state as TodosLoadSuccess).todos));
}
});}
Nothing really changed in terms of recommended approach. All you have to do is call stream property first and listen to it intead of bloc directly. So if you previously listened to blocs / cubits like this:
myBloc.listen((state) => print(state));
all you need to change is call stream first, like this:
myBloc.stream.listen((state) => print(state));

What is the correct way of dealing loading state in flutter?

for example I have 4 future builder in one screen. All of them returns CircularProgress indicator when they are still waiting for the data. In this case my screen gets full of circular progress indicator. Instead of this I want to show loading screen until all of the FutureBuilders gets their data. What is the correct way to handle this ?
The correct way is to wrap the tasks of those 4 futures into one and wait for them to finish using await sequentially, after last await you can simply hide your loader.
If your requirement is different and you can't wait for all 4 task synchronously. You can have an array of int(data type doesn't matter).
and simply initialise it with 4 elements, remove first element on finishing task from future builder.
List<int> _taskList = [0,1,2,3];
remove when one of the future finishes
_taskList.removeAt(0);
Use one Loader Widget in parent with condition
[if(_taskList.length >= 1) Loader(),]
For asynchronous transactions you've to manage some kind of ledger. Preferred way is using await

How can I listen to changes in my bloc stream

I am new to flutter and am trying to build an app using Bloc architecture.
I could manage to figure out a sink, inputting my event bloc.updateNavigation(_selectedItem.value);. I also figured out how to use that data by calling my bloc (is that what its called). I had a TextFormField and changed some settings like this if (bloc.navigationProvider.currentNavigation == 1), if (bloc.navigationProvider.currentNavigation == 2)
There is one thing I couldn't figure out how to do though. I would like to set _Controller.clear(); whenever the bloc changes. So that whenever a user makes a new selection and changes the "currentNavigation" the text controller clears. Is there a way I can find out just when a change is made, rather than having to call by the changes. I would like to do something like if (change is made to currentNavigation) {_Controller.clear();}
My bloc file looks like this:
class NavigationBloc {
final navigationController = StreamController();
NavigationProvider navigationProvider = new NavigationProvider();
Stream get getNavigation => navigationController.stream;
void updateNavigation(int navigation) {
navigationProvider.updateNavigation(navigation);
navigationController.sink.add(navigationProvider.currentNavigation);
}
void dispose() {
navigationController.close();
}
}
final bloc = NavigationBloc();
Streams have a .listen(..) method which gets called everytime data is passed into the stream.
For better usage, use the flutter_bloc package. It provides widgets to provide a bloc to all descendants (BlocProvider) and widgets to listen to state-changes (BlocListener). There you don't have to mess with streams at all and there are a few plugins that help creating the boilerplate code.

Async/Await/then in Dart/Flutter

I have a flutter application where I am using the SQFLITE plugin to fetch data from SQLite DB. Here I am facing a weird problem. As per my understanding, we use either async/await or then() function for async programming.
Here I have a db.query() method which is conducting some SQL queries to fetch data from the DB. After this function fetches the data, we do some further processing in the .then() function. However, in this approach, I was facing some issues. From where I am calling this getExpensesByFundId(int fundId)function, it doesn't seem to fetch the data properly. It's supposed to return Future> object which will be then converted to List when the data is available. But when I call it doesn't work.
However, I just did some experimentation with it and added "await" keyword in front of the db.query() function and somehow it just started to work fine. Can you explain why adding the await keyword is solving this issue? I thought when using .then() function, we don't need to use the await keyword.
Here are my codes:
Future<List<Expense>> getExpensesByFundId(int fundId) async {
Database db = await database;
List<Expense> expenseList = List();
// The await in the below line is what I'm talking about
await db.query(expTable,where: '$expTable.$expFundId = $fundId')
.then((List<Map<String,dynamic>> expList){
expList.forEach((Map<String, dynamic> expMap){
expenseList.add(Expense.fromMap(expMap));
});
});
return expenseList;
}
In simple words:
await is meant to interrupt the process flow until the async method has finished.
then however does not interrupt the process flow (meaning the next instructions will be executed) but enables you to run code when the async method is finished.
In your example, you cannot achieve what you want when you use then because the code is not 'waiting' and the return statement is processed and thus returns an empty list.
When you add the await, you explicitly say: 'don't go further until my Future method is completed (namely the then part).
You could write your code as follows to achieve the same result using only await:
Future<List<Expense>> getExpensesByFundId(int fundId) async {
Database db = await database;
List<Expense> expenseList = List();
List<Map<String,dynamic>> expList = await db.query(expTable,where: '$expTable.$expFundId = $fundId');
expList.forEach((Map<String, dynamic> expMap) {
expenseList.add(Expense.fromMap(expMap));
});
return expenseList;
}
You could also choose to use only the then part, but you need to ensure that you call getExpensesByFundId properly afterwards:
Future<List<Expense>> getExpensesByFundId(int fundId) async {
Database db = await database;
List<Expense> expenseList = List();
return db.query(expTable,where: '$expTable.$expFundId = $fundId')
.then((List<Map<String,dynamic>> expList){
expList.forEach((Map<String, dynamic> expMap){
expenseList.add(Expense.fromMap(expMap));
});
});
}
// call either with an await
List<Expense> list = await getExpensesByFundId(1);
// or with a then (knowing that this will not interrupt the process flow and process the next instruction
getExpensesByFundId(1).then((List<Expense> l) { /*...*/ });
Adding to the above answers.
Flutter Application is said to be a step by step execution of code, but it's not like that.
There are a lot of events going to be triggered in the lifecycle of applications like Click Event, Timers, and all. There must be some code that should be running in the background thread.
How background work execute:
So there are two Queues
Microtask Queue
Event Queue
Microtask Queue runs the code which not supposed to be run by any event(click, timer, etc). It can contain both sync and async work.
Event Queue runs when any external click event occurs in the application like Click event, then that block execution done inside the event loop.
The below diagram will explain in detail how execution will proceed.
Note: At any given point of application development Microtask queue will run then only Event Queue will be able to run.
When making class use async for using await its simple logic to make a wait state in your function until your data is retrieve to show.
Example: 1) Its like when you follow click button 2) Data first store in database than Future function use to retrieve data 3) Move that data into variable and than show in screen 4) Variable show like increment in your following/profile.
And then is use one by one step of code, store data in variable and then move to next.
Example: If I click in follow button until data store in variable it continuously retrieve some data to store and not allow next function to run, and if one task is complete than move to another.
Same as your question i was also doing experiment in social media flutter app and this is my understanding. I hope this would help.
A Flutter question from an answer from your answer.
await is meant to interrupt the process flow until the async method has finished. then however does not interrupt the process flow but enables you to run code when the async method is finished. So, I am asking diff. between top down & bottom down process in programming.

Sharing cold and hot observables

I'm confused by the behavior of a shared stream that is created using Rx.Observable.just.
For example:
var log = function(x) { console.log(x); };
var cold = Rx.Observable
.just({ foo: 'cold' });
cold.subscribe(log); // <-- Logs three times
cold.subscribe(log);
cold.subscribe(log);
var coldShare = Rx.Observable
.just({ foo: 'cold share' })
.share();
coldShare.subscribe(log); // <-- Only logs once
coldShare.subscribe(log);
coldShare.subscribe(log);
Both streams only emit one event, but the un-shared one can be subscribed to three times. Why is this?
I need to "fork" a stream but share its value (and then combine the forked streams).
How can I share the value of a stream but also subscribe to it multiple times?
I realize that this is probably related to the concept of "cold" and "hot" observables. However:
Is the stream created by Rx.Observable.just() cold or hot?
How is one supposed to determine the answer to the previous question?
Is the stream created by Rx.Observable.just() cold or hot?
Cold.
How is one supposed to determine the answer to the previous question?
I guess the documentation is the only guide.
How can I share the value of a stream but also subscribe to it multiple times?
You are looking for the idea of a connectable observable. By example:
var log = function(x) { console.log(x); };
var coldShare = Rx.Observable
.just({ foo: 'cold share' })
.publish();
coldShare.subscribe(log); // Does nothing
coldShare.subscribe(log); // Does nothing
coldShare.subscribe(log); // Does nothing
coldShare.connect(); // Emits one value to its three subscribers (logs three times)
var log = function(x) {
document.write(JSON.stringify(x));
document.write("<br>");
};
var coldShare = Rx.Observable
.just({ foo: 'cold share' })
.publish();
coldShare.subscribe(log); // <-- Only logs once
coldShare.subscribe(log);
coldShare.subscribe(log);
coldShare.connect();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.7/rx.all.min.js"></script>
The example above logs three times. Using publish and connect, you essentially "pause" the observable until the call to connect.
See also:
How do I share an observable with publish and connect?
Are there 'hot' and 'cold' operators?
I don-t understand your first question, but about the last one, as I have been having problem getting that one too:
Rxjs implementation of Observables/Observers is based on the observer pattern, which is similar to the good old callback mechanism.
To exemplify, here is the basic form of creating an observable (taken from the doc at https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/create.md)
var source = Rx.Observable.create(function (observer) {
observer.onNext(42);
observer.onCompleted();
// Note that this is optional, you do not have to return this if you require no cleanup
return function () {
console.log('disposed');
};
});
Rx.Observable.create takes as argument a function (say factory_fn to be original) which takes an observer. Your values are generated by a computation of your choice in the body of factory_fn, and because you have the observer in parameter you can process/push the generated values when you see fit. BUT factory_fn is not executed, it is just registered (like a callback would). It will be called everytime there is a subscribe(observer) on the related observable (i.e. the one returned by Rx.Observable.create(factory_fn).
Once subscription is done (creation callback called), values flow to your observer according to the logic in the factory function and it remains that way till your observable completes or the observer unsubscribes (supposing you did implement an action to cancel value flow as the return value of factory_fn).
What that basically means is by default, Rx.Observables are cold.
My conclusion after using quite a bit of the library, is that unless it is duely documented, the only way to know FOR SURE the temperature of an observable is to eye the source code. Or add a side effect somewhere, subscribe twice and see if the side effect happens twice or only once (which is what you did). That, or ask on stackoverflow.
For instance, Rx.fromEvent produce hot observables, as you can see from the last line in the code (return new EventObservable(element, eventName, selector).publish().refCount();). (code here : https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/linq/observable/fromevent.js). The publish operator is among those operators which turns a cold observable into a hot one. How that works is out of scope so I won-t detail it here.
But Rx.DOM.fromWebSocket does not produce hot observables (https://github.com/Reactive-Extensions/RxJS-DOM/blob/master/src/dom/websocket.js). Cf. How to buffer stream using fromWebSocket Subject
Confusion often comes I think from the fact that we conflate the actual source (say stream of button clicks) and its representation (Rx.Observable). It is unfortunate when that happens but what we imagine as hot sources can end up being represented by a cold Rx.Observable.
So, yes, Rx.Observable.just creates cold observables.