fluttersharedpreferencesflutter-sharedpreference

Flutter shared_preferences sometimes not initialized when using the Singleton pattern - one instance across all files


So I have created a class for using shared_preferences with a singleton pattern, so I can use one instance across all my files.

And almost all of the time it works flawlessly, but occasionally, when I am not using the app but it is still open in the background, and then open it, I get a gray screen because my _prefs can't get the data (probably because it isn't initialized). I can't debug test it because it only appears in release mode. I am not using any more methods from shared_preferences, like SharedPreferences.clear() or SharedPreferences.setMockInitialValues({});. Also, I am only initializing them in my main() function (code given below).

I am wondering if I am doing something wrong, not correctly initializing it or not checking if it is initialized before setting and retrieving data. Does anyone have any clue what may be wrong? Maybe I need to take a different approach?

custom class:

class SharedUserData {
  // home screen dailies
  static const String _APP_CURRENT_DATA_SEED = 'app_current_data_seed';
  static const String _CHOSEN_DAILY_PHYSICS_QUESTION_ANSWER_INDEX= 'chosen_daily_physics_question_index';

  SharedPreferences? _prefs;

  // SINGLETON
  SharedUserData._privateConstructor();
  static final SharedUserData _instance = SharedUserData._privateConstructor();
  static SharedUserData get instance => _instance;


  Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
  }

  void initializeHomeScreenDailies() {
    int? writtenDateSeed = readCurrentDataSeed();
    int calculatedDateSeed = calculateCurrentDateSeed();

    if(writtenDateSeed == null || writtenDateSeed != calculatedDateSeed) {
      writeCurrentDataSeed(calculatedDateSeed);
      writeChosenDailyPhysicsQuestionAnswerIndex(-1);
    }

    printAllData();
  }


  // READ METHODS
  int? readCurrentDataSeed() {
    assert(_prefs != null, 'SharedPreferences not initialized. Call init() first.');
    return _prefs!.getInt(_APP_CURRENT_DATA_SEED);
  }
  int? readChosenDailyPhysicsQuestionAnswerIndex() {
    assert(_prefs != null, 'SharedPreferences not initialized. Call init() first.');
    return _prefs!.getInt(_CHOSEN_DAILY_PHYSICS_QUESTION_ANSWER_INDEX);
  }

  // WRITE METHODS
  Future<void> writeCurrentDataSeed(int currentDataSeed) async {
    assert(_prefs != null, 'SharedPreferences not initialized. Call init() first.');
    await _prefs!.setInt(_APP_CURRENT_DATA_SEED, currentDataSeed);
  }
  Future<void> writeChosenDailyPhysicsQuestionAnswerIndex(int chosenDailyPhyscisQuestionAnswerIndex) async {
    assert(_prefs != null, 'SharedPreferences not initialized. Call init() first.');
    await _prefs!.setInt(_CHOSEN_DAILY_PHYSICS_QUESTION_ANSWER_INDEX, chosenDailyPhyscisQuestionAnswerIndex);
  }
}

I am initializing it in my main.dart file like so:

void main() {
  initializeSharedPreferences();

  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

void initializeSharedPreferences() async {
  WidgetsFlutterBinding.ensureInitialized();

  await SharedUserData.instance.init();
  SharedUserData.instance.initializeHomeScreenDailies();
}

And using like (in a function):

final int? seed = SharedUserData.instance.readCurrentDataSeed();

I tried researching it only but it seems like I can't find anywhere where this problem had also occured.


Solution

  • Since you call an async function from main, you should make main also async and use await to wait for initializeSharedPreferences to complete.

    Try this:

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await initializeSharedPreferences(); 
      runApp(MyApp());
    }
    
    Future<void> initializeSharedPreferences() async {
      await SharedUserData.instance.init();
      SharedUserData.instance.initializeHomeScreenDailies();
    }