I have a _downloadInIsolate(List args) function that works in an isolate. I call it from the VideoDownloadScreen widget. In this widget, after the code responsible for creating and communicating with the isolate, I have the following code:
_receivePort.close();
_isolate.kill();
if (_progress == 100.0) {
Provider.of<AppStateManager>(context, listen: false)
.completeDownloadingAssets(true);
} else {
Provider.of<AppStateManager>(context, listen: false)
.completeDownloadingAssets(false);
}
Works well, but the user can close the widget and then this code will never execute (_downloadInIsolate() still working of course, but the user has no information that the download is finished). I use information about whether assets have been downloaded in several widgets, so I need the Provider.
I only found this link. Is this the only solution? Creating ReceivePort in AppStateManager (in my case) and registering it with IsolateNameServer.registerPortWithName? And then in the isolate find port with IsolateNameServer.lookupPortByName and send a download completion message?
Do you have any other suggestions?
What about killing the isolate at the end of the process?
Related
Inside my Flutter app I am looking to inject a timer function that will execute a periodic call to an underlying service every half hour.
Currently my app is using a Provider class to hold all the business logic, including a method that calls the underlying service. This method makes a call to the service, updates the provider data properties with the results and then calls 'notifyListeners'.
To institute a periodic refresh of this process, I am thinking about using a custom stream that generates a periodic output of a value. A listener can then pick up the stream change, and then call the provider method.
My question is about how best to implement this. Should I set up the stream and listener inside the Provider class. Or should I be using a Stream builder as part of the widget hierarchy?
In the example code below, I have added a periodic streamer to simulate the timer as part of the screen setup. Is this a sound way of doing things?
#override
void didChangeDependencies() {
super.didChangeDependencies();
JourneyProvider aProvider = Provider.of<JourneyProvider>(context, listen: false);
print('initial - ' + DateTime.now().timeZoneName);
aProvider.refreshCollectors();
_timerStream = Stream<DateTime>.periodic(Duration(minutes:30,),(_) {return DateTime.now();});
_sub = _timerStream.listen((dt) {
print(dt);
aProvider.refreshCollectors();
});
}
I used the BLOC architecture inside my app. One method inside my bloc resolves a picked address-ID from a listView and return the city name. After this I want my Text-Editing-Controller to have this picked value and show it on the texteditingfield:
The BlocA uses a simple ID and look it up using Google-Places API. BlocA then return a new state with the resolved address as a state property. Running the code below did not show the correct city after tapping on a listView-item.
Await do not work unfortunately.
// ListView.builder code
onTap: () {
context
.read<BlocA>()
.add(PickAddress(id: state.searchResults[index].placeId));
FocusScope.of(context).unfocus(); //hide Keyboard
_textController.value = TextEditingValue(text: state.address.city);
the code its not clear enough
remember to use "await" you have to set Async on onTap like this
onTap: () async { ....
if you did that and still not working send the whole code
Do you know what your Bloc actually returns? Use the debugger and step through your code to see what actually happens in your code.
When it comes to using Bloc's to build your state you usually don't access the state with by calling .state.
The line _textController.value = TextEditingValue(text: state.address.city); is not the way you access your state. When the Bloc sends the new state, the widget will rebuild and render based on the new state.
My problem is that flutter Hive cannot be opened from multiple isolates. So I want the data fetched in workmananger task to be sent to the main isolate where the hive box is opened and modify it there to avoid corrupting it. I should take into consideration when the app is alive and when it is not => for example when the app is not alive, I edit the hive file directly since it will be only opened in the workmanager isolate whereas if the app is not alive I send the data to the main isolate and edit the hive file. My problem is that I do not know how to track lifecycle within the workmanager task and I do not know how to send the data to the main isolate. Any workarounds or solutions for the problem or how to code the above?
After some research the solution is as follows:
First you register a port on the initialization of the main isolate like this:
void listenToUpdatesFromWorkManager(String name) { var port = ReceivePort(); IsolateNameServer.registerPortWithName(port.sendPort, name); }
You give it a unique name in order to identify it from any other opened isolates like this:
SendPort sendPort = IsolateNameServer.lookupPortByName(name);
Within a flutter app I need to do periodic processing of a resource (actually a sembast database) which is not multi-process safe but does support atomic transactions within a single process. In this context when I say process I mean Isolate (I think!).
I would also like to try and improve robustness by closing (to encourage flush to storage) and re-opening the database when the app lifecycle state allows.
I have implemented what I think should do this, and it seems to work for simples testing so I am soliciting criticism and suggestions to find out what I've missed, or misunderstood.
Attached a (summarised extract) of what I have at the moment.
class _SomethingState extends State<Something> with WidgetsBindingObserver {
// --- the backgroud processing stuff
bool backgroundCanRun = true;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
backgroundCanRun = true;
unawaited(runBackgroundPeriodically());
}
Future<void> runBackgroundPeriodically() async {
while (backgroundCanRun) {
doBackgroundWork();
await Future.delayed(Duration(minutes: 5));
}
}
// deal with app state changes
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.resumed:
openSembastDatabase();
backgroundCanRun = true;
unawaited(runBackgroundPeriodically());
break;
case AppLifecycleState.inactive:
backgroundCanRun = false;
closeSembastDatabase();
break;
case AppLifecycleState.paused:
case AppLifecycleState.suspending:
break;
}
}
}
Somehow I feel like I must be re-inventing the wheel here but searches have so far not made me any the wiser.
Interesting. Indeed sembast is for single process/isolate only. Closing the database won't force flushing (data is always saved to disk as soon as possible). The main benefit I see in what you are doing is that it will definitely save memory space (as sembast hold everything in memory). However re-opening could take a lot of time if the database is big and you will need to handle this close/open state in your app code which could be a pain.
So, following Alex's hint, I plunged through the sembast code and I now understand that sembast appends lines to its file store with the sequence open/write/flush/close. This seems to be about all that can be done in userland to ensure that the data reaches the physical storage.
However, it's turtles all the way down and I do not know what the dart runtime does, nor the underlying android system. All I do know is that there is no point in me doing close/open on the sembast database in response to android lifecycle changes (unless it is to save memory, as Alex commented).
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.