Using SharedPreferences in Flutter effortlessly [UPDATED]
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