Using SharedPreferences in Flutter effortlessly [UPDATED]
Simon Pham
Posted on July 24, 2020
Hi folks đź‘‹
I have release a new package easy_hive, which is a better and simpler approach for storing key-value data instead of using shared_preferences. Please have a look.
Thank you so much!
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] https://pub.dev/packages/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] https://pub.dev/packages/shared_preferences#-installing-tab-) package.
First, we need to define a class to store the SharedPreferences instance. Let’s name it SharedPrefs.
// utils/shared_prefs.dart
class SharedPrefs {
static 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.dart
class SharedPrefs {
static SharedPreferences _sharedPrefs;
init() async {
if (_sharedPrefs == null) {
_sharedPrefs = await SharedPreferences.getInstance();
}
}
}
final sharedPrefs = SharedPrefs();
And call it in main() function.
// main.dart
Future<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.dart
class SharedPrefs {
static SharedPreferences _sharedPrefs;
init() async {
if (_sharedPrefs == null) {
_sharedPrefs = await SharedPreferences.getInstance();
}
}
String get username => _sharedPrefs.getString(keyUsername) ?? "";
set username(String value) {
_sharedPrefs.setString(keyUsername, value);
}
}
final sharedPrefs = SharedPrefs();
// constants/strings.dart
const 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 https://blog.simonit.dev
Posted on July 24, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.