title: Flutter: Initialising variables on startup
body: <p>I'm trying to use the values saved in the SharedPreferences to initialise several variables in my app. In Flutter, the SharedPreferences are asynchronous so it results in the variables initialising later on in the code which is creating problems with my app as some of the variables are null when the build method is called.</p>
  
  <p>Here is a small test Flutter app I wrote to demonstrate this issue:</p>
  
  <pre><code>import 'package:flutter/material.dart';
  import 'package:flutter/cupertino.dart';
  import 'package:path_provider/path_provider.dart';
  import 'dart:io';
  import 'dart:convert';
  import 'package:shared_preferences/shared_preferences.dart';
  
  class TestingApp extends StatefulWidget {
  
    TestingApp() {}
  
    @override
    State&lt;StatefulWidget&gt; createState() {
  // TODO: implement createState
      return new _CupertinoNavigationState();
    }
  }
  
  class _CupertinoNavigationState extends State&lt;TestingApp&gt; {
  
    int itemNo;
  
    @override
    void initState() {
      super.initState();
  //    SharedPreferences.getInstance().then((sp) {
  //      sp.setInt("itemNo", 3);
  //    });
      SharedPreferences.getInstance().then((sp)  {
        print("sp " + sp.getInt("itemNo").toString());
        setState(() {
          itemNo = sp.getInt("itemNo");
        });
      });
      print("This is the item number " + itemNo.toString());
    }
  
    @override
    Widget build(BuildContext context) {
      // TODO: implement build
      print("item number on build " + itemNo.toString());
      return new Text("Hello");
    }
  }
  </code></pre>
  
  <p>This is the result in the console:</p>
  
  <pre><code>Performing full restart...
  flutter: This is the item number null
  flutter: item number on build null // build method being called and variable is null
  Restarted app in 1 993ms.
  flutter: sp 3
  flutter: item number on build 3
  </code></pre>
  
  <p>You can see that when I tried to fetch the variable from SharedPreferences on startup, since SharedPreference is async, the itemNo is null. Then the app runs the build method and runs the build method on the itemNo = null which is resulting in crashes in the app.</p>
  
  <p>Once it fetches the value from SharedPreferences, I call setState which then calls the build method again with the correct value. However, the initial call to build with the itemNo = null should not have happened.</p>
  
  <p>I wish there was a synch method for SharedPreferences but it doesn't seem to exist. How do I run the app so that the variables are initialised properly in Flutter on startup?</p>
  
  <p>I tried to solve this through using a synchronous method to initialise my variables by writing to a json file and then reading from it using the following short Flutter test app - to me, this appears to be overkill for saving a variable to initialise but I still gave it a try:</p>
  
  <pre><code>import 'package:flutter/material.dart';
  import 'package:flutter/cupertino.dart';
  import 'package:path_provider/path_provider.dart';
  import 'dart:io';
  import 'dart:convert';
  import 'package:shared_preferences/shared_preferences.dart';
  
  class TestingApp extends StatefulWidget {
  
    TestingApp() {}
  
    @override
    State&lt;StatefulWidget&gt; createState() {
  // TODO: implement createState
      return new _CupertinoNavigationState();
    }
  }
  
  class _CupertinoNavigationState extends State&lt;TestingApp&gt; {
  
    int itemNo;
    File  jsonFile;
    String fileName = "items.json";
    Directory dir;
    bool fileExists = false;
  
  void createFile(Map content, Directory dir, String fileName) {
  //  print("Creating file for category " + dir.path);
    File file = new File(dir.path + "/" + fileName);
    file.createSync();
    fileExists = true;
    file.writeAsStringSync(json.encode(content));
  }
  
  void writeToFile(int itemNo) {
  //  print("Writing to category file");
    Map itemMap = new Map();
    itemMap['item'] = itemNo;
    if (fileExists) {
      print("category file exists");
      Map jsonFileContent = json.decode(jsonFile.readAsStringSync());
      jsonFileContent.addAll(itemMap);
      jsonFile.writeAsStringSync(json.encode(itemMap));
    } else {
      print("category File does not exists");
      getApplicationDocumentsDirectory().then((Directory directory) {
        dir = directory;
        createFile(itemMap, dir, fileName);
      });
  
    }
  }
  
  fetchSavedItemNo() {
    //load the currency from the saved json file.
    getApplicationDocumentsDirectory().then((Directory directory) {
      dir = directory;
      jsonFile = new File(dir.path+ "/" + fileName);
      fileExists = jsonFile.existsSync();
      setState(() {
        if (fileExists)
          itemNo = json.decode(jsonFile.readAsStringSync())['item'];
        print("fetching saved itemNo " +itemNo.toString());
        if (itemNo == null) {
          itemNo = 0;
        }
      });
  
  
      return itemNo;
      //else the itemNo will just be 0
    });
  }
  
    @override
    void initState() {
      super.initState();
      writeToFile(3);
      setState(() {
        itemNo = fetchSavedItemNo();
      });
  
    }
  
    @override
    Widget build(BuildContext context) {
      // TODO: implement build
      print("item number on build " + itemNo.toString());
      return new Text("Hello");
    }
  }
  </code></pre>
  
  <p>I still have the results where the build method is being called before the variables are fully initialised which is causing app crashes.</p>
  
  <pre><code>Performing full restart...
  flutter: category File does not exists
  flutter: item number on build null   // the build method is called here
  Restarted app in 1 894ms.
  flutter: fetching saved itemNo 3
  flutter: item number on build 3
  </code></pre>
  
  <p>How do I initialise variables in Flutter on app startup?</p>