How to reload screen on future data

Viewed 182

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

1 Answers

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

Related