DEV Community

Simon Pham
Simon Pham

Posted on • Edited on

12 6

Using SharedPreferences in Flutter effortlessly [UPDATED]


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);
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

And call it in main() function.

// main.dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await sharedPrefs.init();
  runApp(
    MyApp(),
  );
}
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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}"),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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}"),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

Top comments (5)

Collapse
 
fluttercorner profile image
FlutterCorner •
Collapse
 
godzalo profile image
Nicolas M •

Just what I needed, much appreciated.

Collapse
 
idrisadeyemi01 profile image
Idris Idris •

Nice read

Collapse
 
nicholas_moen profile image
arcanemachine •

Thank you so much for this!

Collapse
 
miguelalvesmiguel profile image
MiguelAlvesMiguel •

Thank you for this!

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

đź‘‹ Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay