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

Share:
166
shanthosh
Author by

shanthosh

Updated on December 24, 2022

Comments

  • shanthosh
    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 filled CircularProgressIndicator 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
      shanthosh almost 4 years
      Yeah, this is what I wanted. I'll try the same in my case
    • shanthosh
      shanthosh almost 4 years
      I tried the first example. I added future = loadApps() in initState() and added FutureBuilder to build of AppScreenC. Progress bar is displayed every time I visit first tab. Is it because of calling a StatefulWidget in TabBarView? My tab bar view looks like TabBarView( children: [AppScreenC(), //Other tabs]).
    • shanthosh
      shanthosh almost 4 years
      Second example is working perfectly.
    • shanthosh
      shanthosh almost 4 years
  • shanthosh
    shanthosh almost 4 years
    Thank 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
    Morez almost 4 years
    You 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 run loadApps() 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 the circularProgressIndicator if apps are not null
  • shanthosh
    shanthosh almost 4 years
    Will there be any performance issue by making app as a global variable
  • Morez
    Morez almost 4 years
    Not 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-dar‌​t for creating global variables
  • Scott Godfrey
    Scott Godfrey almost 4 years
    If it's constantly being reloaded or is accessed in too many widgets, then yes. Otherwise, it should be fine.
  • Morez
    Morez almost 4 years
    Also have a look at stackoverflow.com/a/10525602/12828249 for global variables disadvantages