How to view all Flutter app cache files and objects? - flutter

Listview / PageView Widgets do cache data and I don't know where, as well as Image.network and another bunch of widgets do cache data.
I know that you may clear cache by these methods below :-
and the methods mentioned in this thread
How to clear app cache programmatically on Flutter
setState(() {
imageCache.clear();
imageCache.clearLiveImages();
PaintingBinding.instance.imageCache.clear();
});
But I'm not sure that I have deleted ALL cached data allover the app,
Are these enough to clear all cached data or there are more ?
and How can you list and view all the cached data that are misteriously hidden somewhere I don't know where !?
please excuse my ignorance

This would not be the answer as I still don't know how to list and read all the data
but I could gather number of functions that make up a good cocktail to cleanup my heavy app
Future<void> deleteAllCacheThereIsInThisHeavyLaggyAppThatSucksMemoryLikeABlackHole() async {
await Future.wait(<Future>[
getTemporaryDirectory().then((Directory directory) async {
await Directory(directory.path).delete(recursive: true);
}),
getApplicationDocumentsDirectory().then((Directory directory) async {
await Directory(directory.path).delete(recursive: true);
}),
DefaultCacheManager().emptyCache(),
]);
imageCache.clear();
imageCache.clearLiveImages();
PaintingBinding.instance.imageCache.clear();
}
notice that i used this package https://pub.dev/packages/flutter_cache_manager as well
Please feel free to add any method that deletes any hidden magical cached file that I do not know about
I prefer to call these methods multiple times rather than having my app too heavy to navigate

Related

Best place to save data in Flutter

I am using Clean Architecture in Flutter with Getx as State Management.
I have a list that i am taking from backend and i am showing it in UI and user can do some changes to this list as well.
where is the best place to store the data that is taken from backend and show from and change data.
right now in this example
the data is stored in Getx Controller.is this approach correct?or there is a better way?
class Controller extends GetxController{
List<Data> lsData = [];
fetchApi()async{
lsData = await fetchApi();
update();
}
add(Data data){
lsData.add(data);
update();
}
postApi()async{
await post(lsData);
}
}
there are many ways to save data in flutter, if you want to save data local there is Hive(i like this), Share preference, SQlite,GetX Storage and more
if you want to save data online I recommend Firestore
Hope this can be helpful :)

Navigating to another screen gets slower and slower each time i repeat click, go back, click, go back

I have a Navigator.push and MaterialPageRoute() to navigate to another screen. But navigation to other screens gets slower and slower becuase in my initState() i have a method which initializes the json data which i show each time i navigate to another screen. The json data is big and i only use one file with big json data which has objects and each object is shown i different screens. In my usecase i have to use one file with big json data.
In my initState() i initialize the data i grabbed with the method setTestData() and inside this method i set the data inside an object:
late Map<String, dynamic> grabbedData = {};
setTestData() async {
await TestData()
.getTestData()
.then((result) => setState(() => grabbedData = result));
}
#override
initState() {
setTestData();
super.initState();
}
In my view i can for example navigate to another screen and then show different objects inside the same object json i grabbed in setTestData(). I only use one view called AppView() to show different screen so when i navigate for example from screen A to B, both A and B screen are shown with AppView() widget. This is necessary for my use case which is irrelevant for this question.
This is the navigation which i use to load another screen and which technacly runs initState() from AppView() again because the previous route is also AppView():
Navigator.push(
context,
MaterialPageRoute(
maintainState: false,
builder: (_) => AppView(
selectedMenuItems:
grabbedData['data']
['views'][
widget.selectedMenuItems[
index]
[
'content']],
)));
But the problem is that each time i navigate to AppView() and click back on my phone to show previous data from AppView() and navigate again, it re-initializes the state and so the proces is slowed after i repeat it a couple of times. How do i solve this problem?
Better to use Provider package for this task. Provider does not rebuild the widget rather it only updates the state of the widget so it is faster. Then, you can get your data or data stream only once at the beginning of your app or a particular screen. No need to generate data each time. Also, provider automatically disposes data when the screen is closed. It is recommended by flutter team as well and an awesome package. For more example, check some YouTube videos. For your particular problem, I think it is better to use provider.value. Check the references and hopefully later on you will understand what is a provider.value object.
If you are generating new data every time then you need to set your provider.value each time where you are using Navigator.push, otherwise if you do not use your provider at the beginning of your app at MaterialApp section then after the push the provider won't be available.
As an example, to add provider inside a push please follow the following code snippet:
Navigator.push(context,
MaterialPageRoute(builder: ((context) {
return StreamProvider<User>.value(
initialData: initialUserData,
value: userDataStream,
child: const UpdateUser(),
);
})));
Next, access the value in the UpdateUser page like this:
final user = Provider.of<User>(context);
If you are not using any data stream then just try the normal ChangeNotifierProvider and get the value from the Consumer. Try some youtube tutorials and you will love it.

How can you get the flutter visible page's state or context?

Sometimes an event (eg upload) starts async while the user is on one page. If they navigate away from that page the task's .then(...) will try to display the result on the page which is no longer visible, eg by means of a toast.
How can I get the context of the currently visible page at the time when the Future completes to display a snackbar, toast or dialog?
EDIT:
I see in the description of oktoast (https://pub.dev/packages/oktoast) that version 2 "Does not need the buildContext to be passed", and further
Quote:
Explain #
There are two reasons why you need to wrap MaterialApp
Because this ensures that toast can be displayed in front of all other
controls Context can be cached so that it can be invoked anywhere
without passing in context
This implies to me that there is a better way by providing a Material app ancestor somewhere....
For a simple Solution I would use the Provider Package. Keyword here is StateManagement (https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple)
The Model would look sth like this:
class UploadModel extends ChangeNotifier {
final bool loading = false;
void uploadXY() async{
loading = true;
// This call tells the widgets that are listening to this model to rebuild.
notifyListeners();
await realUploadStuff();
loading = true;
notifyListeners();
}
}
To start the upload:
Provider.of<UploadModel>(this).uploadXY()
To react if loading-bool changes:
if(Provider.of<UploadModel>(this).loading)...
You can find a simple Example here:
https://github.com/flutter/samples/blob/master/provider_counter/lib/main.dart

My approach to "background" processing in an (android) flutter app, not using another Isolate. Have I got this right?

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).

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.