Flutter Null Safe Config Class with shared_preferences

Issue

In flutter 1.x, I implemented a Config class using the Flutter shared_preferences package; the code looks like this:

import 'package:shared_preferences/shared_preferences.dart';

class Config {
  static final Config _config = Config._internal();

  factory Config() => _config;

  final accessTokenKey = 'accessToken';
  String _accessToken;

  SharedPreferences prefs;

  Config._internal() {
    loadData();
  }

  void loadData() async {
    prefs = await SharedPreferences.getInstance();
    _accessToken = prefs.getString(accessTokenKey) ?? '';
  }

  String get accessToken {
    return _accessToken;
  }

  set accessToken(String accessToken) {
    _accessToken = accessToken;
    _saveString(accessTokenKey, accessToken);
  }

  _saveString(String key, String value, {String printValue = ''}) {
    String printVal = printValue.length > 0 ? printValue : value;
    prefs.setString(key, value);
  }

}

I’m creating a new project in Flutter 2.x and trying to use the same code, but due to changes associated with null safety I’m having some difficulty getting the updated code just right.
The updated documentation for the package says to initialize the _prefs object like this:

Future<SharedPreferences> _prefs = SharedPreferences.getInstance();

Then create a local prefs object using:

final SharedPreferences prefs = await _prefs;

This is fine, but I don’t want to have to make every class method that uses shared_preferences async then recreate the variable. At the same time I can’t create it as a class variable without initializing it first. Can someone please show me a cleaner way to do this, or do I just have to redeclare it every time I use it?
Also, how do I initialize the config object in my other classes? In my 1.x code, I would just do this:

final Config config = new Config();

then start accessing the properties of the config object. How do I initialize it with all of the async code in the class now?

Here’s where the updated code is today:

import 'package:shared_preferences/shared_preferences.dart';

import '../models/device.dart';

class Config {
  static final Config _config = Config._internal();

  factory Config() => _config;

  final accessTokenKey = 'accessToken';

  String _accessToken = '';

  Future<SharedPreferences> _prefs = SharedPreferences.getInstance();

  Config._internal() {
    print('Config constructor');
    loadData();
  }

  Future<void> loadData() async {
    final SharedPreferences prefs = await _prefs;
    _accessToken = prefs.getString(accessTokenKey) ?? '';
  }

  String get accessToken {
    return _accessToken;
  }

  set accessToken(String accessToken) {
    _accessToken = accessToken;
    _saveString(accessTokenKey, accessToken);
  }

  _saveString(String key, String value, {String printValue = ''}) {
    String printVal = printValue.length > 0 ? printValue : value;
    print('Config: _saveString("$key", "$printVal")');
    final SharedPreferences prefs = await _prefs;
    prefs.setString(key, value);
  }

}

Solution

You can get instance of SharedPreferences as static field in init method:

static SharedPreferences? _prefs; //or: static late SharedPreferences _prefs;

static init() async {
  _prefs = await SharedPreferences.getInstance();
}

And call init() somewhere like in build() method of first widget run, for once.Now you can use _prefs everywhere as you want.
If I want to show you a complete class to use SharedPreferences, it looks like this:

import 'package:shared_preferences/shared_preferences.dart';

class SharedPreferencesRepository {
  static SharedPreferences? _prefs;

  static init() async {
    _prefs = await SharedPreferences.getInstance();
  }

  static putInteger(String key, int value) {
    if (_prefs != null) _prefs!.setInt(key, value);
  }

  static int getInteger(String key) {
    return _prefs == null ? 0 : _prefs!.getInt(key) ?? 0;
  }

  static putString(String key, String value) {
    if (_prefs != null) _prefs!.setString(key, value);
  }

  static String getString(String key) {
    return _prefs == null ? 'DEFAULT_VALUE' : _prefs!.getString(key) ?? "";
  }

  static putBool(String key, bool value) {
    if (_prefs != null) _prefs!.setBool(key, value);
  }

  static bool getBool(String key) {
    return _prefs == null ? false : _prefs!.getBool(key) ?? false;
  }
}

I hope this useful for you.

Answered By – Saeed Fekri

Answer Checked By – Katrina (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *