Using SharedPreferences in Flutter effortlessly [UPDATED]

Simon Pham
3 min readJul 15, 2020

We all know that SharedPreferences is a key/value store where you can read and store data very easily. It’s being used in most apps nowadays.

In Flutter, there’s a package named shared_preferences that helps us deal with key/value data…

Hi. My name is Simon Pham (Cường - in Vietnamese). I have been working with Flutter since January 2019. I decided to write this blog post to share my experience in handling SharedPreferences in Flutter.

We all know that SharedPreferences is a key/value store where you can read and store data very easily. It’s being used in most apps nowadays.

In Flutter, there’s a package named shared_preferences that helps us deal with key/value data. Its documentation gives us something like this:

_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
await prefs.setInt('counter', counter);
}

It is really inconvenient to get the SharedPreferences instance asynchronously every time we need it. To increase reusability and reduce boilerplate code, I have an approach to save the instance of SharedPreferences and create getters/setters for the preference keys to use them anywhere in the app.

“Talk is cheap. Show me the code.” ~ Linus Torvalds

I will assume that you have already added the shared_preferences package.

First, we need to define a class to store the SharedPreferences instance. Let’s name it SharedPrefs.

// utils/shared_prefs.dartclass SharedPrefs {
late final SharedPreferences _sharedPrefs;
}

final sharedPrefs = SharedPrefs();

Easy, right?

Then, we will define an init() method to get the SharedPreference instance and store it to the _sharedPrefs variable in the SharedPrefs class.

// utils/shared_prefs.dartclass SharedPrefs {
late final SharedPreferences _sharedPrefs;
Future<void> init() async {
_sharedPrefs = await SharedPreferences.getInstance();
}

}

final sharedPrefs = SharedPrefs();

And call it in main() function.

// main.dartFuture<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await sharedPrefs.init();
runApp(
MyApp(),
);
}

Why called SharedPrefs.init() in the main() function? Because before runApp() is called, the app will show the splash screen. It’s a perfect moment for us to do some essential initialization for the app.

The next step is to define getters & setters for your preference keys. For example, I defined a getter and setter for getting & updating username in SharedPreferences.

// utils/shared_prefs.dartclass SharedPrefs {
late final SharedPreferences _sharedPrefs;

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

String get username => _sharedPrefs.getString(keyUsername) ?? "";

set username(String value) {
_sharedPrefs.setString(keyUsername, value);
}

}

final sharedPrefs = SharedPrefs();
// constants/strings.dartconst String keyUsername = "key_username";

I also create a keyUsername constant for consistency. You will not want to use a String directly like _sharedPrefs.getString(“key_username”) and in another place use _sharedPrefs.setString(“key_user_name”, value) by mistake.

That’s it. Now you can access username ANYWHERE in the app.

class MyApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Text("Hi ${sharedPrefs.username}"),
),
);
}
}

Updated: Use factory

Factory constructors
Use the factory keyword when implementing a constructor that doesn’t always create a new instance of its class. For example, a factory constructor might return an instance from a cache, or it might return an instance of a subtype. Another use case for factory constructors is initializing a final variable using logic that can’t be handled in the initializer list.

class SharedPrefs {
late final SharedPreferences _sharedPrefs;

static final SharedPrefs _instance = SharedPrefs._internal();
factory SharedPrefs() => _instance; SharedPrefs._internal(); Future<void> init() async {
_sharedPrefs = await SharedPreferences.getInstance();
}

// ...
}

// Remove this below line:
// final sharedPrefs = SharedPrefs();

Now change sharedPrefs to SharedPrefs() and you're good to go!

// main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SharedPrefs().init();
runApp(
MyApp(),
);
}

class MyApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Text("Hi ${SharedPrefs().username}"),
),
);
}
}

Thank you for reading my blog. You can check out this gist for the full code.

Have questions? Find me at http://simonpham.github.io

--

--

Simon Pham

An Android enthusiast. Love making great applications using native Android (Kotlin) or Flutter.