Home > Back-end >  await async method in initialize() - json file loading Flutter/Dart
await async method in initialize() - json file loading Flutter/Dart

Time:11-13

I am currently working on a small clicker game for university and its my first time using flutter dart.

Ive created an upgrade tab which displays different upgrades. Thoose upgrades are being stored and loaded from a .json file

Whenever i try to open the upgrade tab, dart throws an exception -> RangeError (index): Index out of range: no indices are valid: 0 This is due to the json not being loaded yet and axeList is empty But due to dart calling build method multiple times its being loaded afterwards

Still... there is a frame with an error. Here is a slowed down gif:

slowed down gif (imgur.com)

Here is my code:

class Upgrades extends StatefulWidget {
  const Upgrades({Key? key}) : super(key: key);

  @override
  State<Upgrades> createState() => _UpgradesState();
}

class _UpgradesState extends State<Upgrades> with Store {
  List axeList = [];

  
  @override
  void initState() {
    readJson();
    super.initState();
  }

  Future<void> readJson() async {
    final String response = await rootBundle.loadString("/axe_list.json");
    final data = await json.decode(response);
    setState(() {
      axeList = data["axe"];
    });
  }

@override
Widget build(BuildContext context) {
  return //I need the content of axeList in this buildMethod

Order:

  • initState()
  • readJson()
  • build() |-> exception because axeList isnt loaded yet
  • readJson ready

The problem is that i cant use async in initState:

  @override
  Future<void> initState() async{
    // Call the readJson method when the app starts & ensure loaded
    await readJson();
    super.initState();
  }

This will resolve in following exception: "_UpgradeState.initState() returned a Future. State.initState() must be a void method without an async keyword"

Loading the json on app startup doesnt seem to be the solution either, because Upgrades will not be called directly at Startup, but at a later point. Also id have to hand over the data in like 7 constructors

How do i ensure the async method is loaded before the build method? thx for answers <3

CodePudding user response:

You can use FutureBuilder widget to load the data asynchronously in the UI.

The following example shows how to use it.

FutureBuilder<List<dynamic>>(
              future: readJson(),
              builder: (context,snapShot){
                switch(snapShot.connectionState){
                  
                  case ConnectionState.none:
                   // show error widget
                    break;
                  case ConnectionState.waiting:
                    // show loading widget
                    break;
                  case ConnectionState.active:
                    // show loading widget
                    break;
                  case ConnectionState.done:
                    // show widget that consumes data
                  //final data = snapShot.data
                    break;
                }
                //error widget if future fails or not handled
               return const Text("Error");
              },
            ),
  1. you don't need to call setState() to build the widget again
  2. make sure the return type of readJson() is same as what you pass to FutureBuilder<List>().

CodePudding user response:

a FutureBuilder will fix your problem, follow with this.

first, replace your method with this:

since we will use FutureBuilder, there is no need to SetState(() {}):

     Future<List> readJson() async {
    final String response = await rootBundle.loadString("/axe_list.json");
    final data = await json.decode(response);
    return data["axe"];
  }

then in the build():

  return FutureBuilder<List>(
  future: readJson(),
  builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }
    if (snapshot.hasData) {
      List axeList = snapshot.data!;

      return YourMainWidget(); // here use axeList to generate the widgets
    }
    return const Text("no data");
  },
);

replace YourMainWidget() with your main widget where you will need the axeList.

  • Related