How to reload screen on future data
166
What you can do is calling setState()
after your Function is done. You need to change loadedApp to return a Future
:
class AppScreenC extends StatefulWidget {
@override
_AppScreenCState createState() => _AppScreenCState();
}
class _AppScreenCState extends State<AppScreenC> {
@override
void initState(){
super.initState();
model.loadApps().then((loadedApps){ //loadApps() will return apps and you don't need loadedApps() method anymore
setState((){ //rebuilds the screen
apps = loadedApps
})});
}
@override
Widget build(BuildContext context) => _buildApps();
Widget _buildApps() => apps != null
? ListView.builder(
itemCount: apps.length,
itemBuilder: (BuildContext context, int index) =>
_buildRow(apps[index]))
: Center(child: CircularProgressIndicator());
Widget _buildRow(ApplicationWithIcon app) {
final saved = model.getApps().contains(app.apkFilePath);
return ListTile(
leading: Image.memory(app.icon, height: 40),
trailing: saved
? Icon(Icons.check_circle, color: Colors.deepPurple[400])
: Icon(Icons.check_circle_outline),
title: Text(app.appName),
onTap: () => setState(() => saved
? model.removeApp(app.apkFilePath)
: model.addApp(app.apkFilePath)),
);
}
}
And your AppModel
will look like this:
class AppModel{
final _saved = Set<String>();
List<Application> apps;
AppModel() {
loadApps();
}
Set<String> getApps() {
return _saved;
}
addApp(String apkPath) {
_saved.add(apkPath);
}
removeApp(String apkPath) {
_saved.remove(apkPath);
}
Future loadApps() async {
apps = await DeviceApps.getInstalledApplications(
onlyAppsWithLaunchIntent: true,
includeSystemApps: true,
includeAppIcons: true);
apps.sort((a, b) => a.appName.compareTo(b.appName));
return Future.value(apps);
}
}
You can also use FutureBuilder
as suggested in the comments
![shanthosh](https://lh6.googleusercontent.com/-d5lh3KQNoKM/AAAAAAAAAAI/AAAAAAAADtg/xvTJctZQbpY/photo.jpg?sz=256)
Author by
shanthosh
Updated on December 24, 2022Comments
-
shanthosh over 1 year
I have
TabBarView
with six tabs. I'm trying to show all the installed apps in first tab.Even after
apps
is filledCircularProgressIndicator
is displayed. Apps are listed once the first tab is revisited.AppScreenC
is called for first tab.final model = AppModel(); class AppScreenC extends StatefulWidget { @override _AppScreenCState createState() => _AppScreenCState(); } class _AppScreenCState extends State<AppScreenC> { List<Application> apps = model.loadedApps(); @override Widget build(BuildContext context) => _buildApps(); Widget _buildApps() => apps != null ? ListView.builder( itemCount: apps.length, itemBuilder: (BuildContext context, int index) => _buildRow(apps[index])) : Center(child: CircularProgressIndicator()); Widget _buildRow(ApplicationWithIcon app) { final saved = model.getApps().contains(app.apkFilePath); return ListTile( leading: Image.memory(app.icon, height: 40), trailing: saved ? Icon(Icons.check_circle, color: Colors.deepPurple[400]) : Icon(Icons.check_circle_outline), title: Text(app.appName), onTap: () => setState(() => saved ? model.removeApp(app.apkFilePath) : model.addApp(app.apkFilePath)), ); } }
AppModel
class has all the necessary methods.class AppModel{ final _saved = Set<String>(); List<Application> apps; AppModel() { loadApps(); } Set<String> getApps() { return _saved; } addApp(String apkPath) { _saved.add(apkPath); } removeApp(String apkPath) { _saved.remove(apkPath); } loadApps() async { apps = await DeviceApps.getInstalledApplications( onlyAppsWithLaunchIntent: true, includeSystemApps: true, includeAppIcons: true); apps.sort((a, b) => a.appName.compareTo(b.appName)); } loadedApps() => apps; }
This is happening because
apps
is null, when the screen was called first time. It loads the apps in background. Upon visiting the screen again, apps are displayed.Any help is welcome
-
shanthosh almost 4 yearsYeah, this is what I wanted. I'll try the same in my case
-
shanthosh almost 4 yearsI tried the first example. I added
future = loadApps()
ininitState()
and addedFutureBuilder
tobuild
ofAppScreenC
. Progress bar is displayed every time I visit first tab. Is it because of calling aStatefulWidget
inTabBarView
? My tab bar view looks likeTabBarView( children: [AppScreenC(), //Other tabs])
. -
shanthosh almost 4 yearsSecond example is working perfectly.
-
shanthosh almost 4 yearsLet us continue this discussion in chat.
-
-
shanthosh almost 4 yearsThank you!!! for the answer. But this code shows
circularProgressIndicator
every time first tab is visited. To overcome that, I added a null check in loadApps().Future loadApps() async { if(apps == null) { // same code } return Future.value(apps); }
Is it right? -
Morez almost 4 yearsYou have 2 options. you can define app as a global variable so when you visit this page (if already visited), then
app
is not null anymore and you don't need to runloadApps()
anymore. You can also visit stackoverflow.com/a/51738269/12828249 to learn how to preserve the screen state so that when you come back to this screen, you don't build the screen again and with that, you won't see thecircularProgressIndicator
ifapps
are not null -
shanthosh almost 4 yearsWill there be any performance issue by making app as a global variable
-
Morez almost 4 yearsNot that I know of but you have to be careful not to change the value somewhere else in the app as global variables are accessible throughout the program. Check out stackoverflow.com/questions/29182581/global-variables-in-dart for creating global variables
-
Scott Godfrey almost 4 yearsIf it's constantly being reloaded or is accessed in too many widgets, then yes. Otherwise, it should be fine.
-
Morez almost 4 yearsAlso have a look at stackoverflow.com/a/10525602/12828249 for global variables disadvantages