Save Data on the Device Using Shared Preferences in Flutter

When developing Flutter applications, it is common to persistently store and retrieve small amounts of data, like user preferences and settings. On Android devices, developers can use Shared Preferences, and on iOS devices the equivalent is NSUserDefaults. Luckily because we are using Flutter we can use the Shared Preferences plugin.

Shared Preferences Plugin

The Shared Preferences plugin makes it very easy to save data in the platform-specific persistent storage. Instead of having to worry about creating different platform-specific implementations. We can just use the Plugin and it will both work on Android and iOS. The plugin will ensure that the proper persistent storage is selected Shared Preferences for Android and NSUserDefaults for iOS.

Keep in mind that Shared Preferences is most suitable for handling small amounts of data. If you plan to store large amounts of data, it is better to use a local database solution.

Install the Shared Preferences Plugin

To install the Shared Preferences plugin to save data on the device, execute the following command within your project directory:

flutter pub add shared_preferences

Once the command is executed, make sure to check your pubspec.yaml file for the added dependencies. You should see the Shared Preferences plugin included in the dependencies section, like this:

dependencies:
  shared_preferences: ^2.2.3

Save Data on the Device

To save data using the Shared Preferences plugin we first need to create an instance of the SharedPreferences class. We can do this by calling the class’s getInstance function.

import 'package:shared_preferences/shared_preferences.dart';

Future<void> main() async {
  SharedPreferences preferences = await SharedPreferences.getInstance();
}

Once, we have the instance we can save the first entry in the persistent storage of the device. Therefore, we can use the setString function. The setString function takes a key and a value.

import 'package:shared_preferences/shared_preferences.dart';

Future<void> main() async {
  SharedPreferences preferences = await SharedPreferences.getInstance();

  await preferences.setString('key', 'value');
}

However, whenever you run the above code you will notice that we run into the following error: Unhandled Exception: Binding has not yet been initialized. This is because we first need to ensure that our widget bindings are initialized.

If you are interested in learning more about the error itself, you can read about it here: The “instance” getter on the ServicesBinding binding mixin is only available once that binding has been initialized.

We can resolve the above error by ensuring that we first call the following function before using the SharedPreferences class: WidgetsFlutterBinding.ensureInitialized

import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  SharedPreferences preferences = await SharedPreferences.getInstance();

  await preferences.setString('key', 'value');
}

Afterward, if we run the code again, you will see that we no longer encounter any errors.

Saving Different Types Using Shared Preferences

In addition to saving strings, we can also save booleans, doubles, integers, and lists of strings.

import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  SharedPreferences preferences = await SharedPreferences.getInstance();

  await preferences.setString('key', 'value');
  await preferences.setBool('bool', true);
  await preferences.setDouble('double', 0.0);
  await preferences.setInt('integer', 1);
  await preferences.setStringList('stringList', [
    'value 1',
    'value 2',
  ]);
}

Now that we know how to save data on the device let us continue retrieving the data from the persistent storage.

Retrieve Data From the Device

To retrieve data from the device’s persistent storage, we can use the get function of the SharedPreferences class. The get function takes a key parameter, which should match a key that was previously used to save data on the device.

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  SharedPreferences preferences = await SharedPreferences.getInstance();

  await preferences.setString('key', 'value');

  print(preferences.get('key'));
}

In the above code snippet, we call the get function with the key that we used to save a string on line 8. If we run the above code, we will see the following result in the terminal:

value

Keep in mind that the return type of the get function is of type Object?.

Returning the Correct Type

If you immediately want to return the correct type you can use the other get functions.

  preferences.getString('key');
  preferences.getBool('key');
  preferences.getDouble('key');
  preferences.getInt('key');
  preferences.getStringList('key');

The above functions will only return a result if the stored value with the designated key was saved with the same type as the get function you are using. If this is not the case, the functions will throw an error.

Delete Data From the Device

Other than setting and getting data with a key, we can also remove the entry associated with that key and check if the key still exists in the persistent storage.

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  SharedPreferences preferences = await SharedPreferences.getInstance();

  await preferences.setString('key', 'value');

  print(preferences.containsKey('key'));

  await preferences.remove('key');

  print(preferences.containsKey('key'));
}

In the above code, we first save a value in the persistent storage. Afterwards, we check if the key exists. Then, we remove the entry from the persistent storage by calling the remove function with the same key. Finally, we check if the persistent storage still contains our entry.

When we run the code, we will get the following result:

true
false

This means that we successfully removed the entry from the persistent storage. Additionally, it is possible to remove all entries at once from the persistent storage.

Clear the Persistent Storage

To completely clear the persistent storage we can call the class’s clear function.

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  SharedPreferences preferences = await SharedPreferences.getInstance();

  await preferences.setString('key', 'value');
  await preferences.setString('test', 'value');

  await preferences.clear();

  print(preferences.containsKey('key'));
  print(preferences.containsKey('test'));
}

In the above example, we first save two entries in the persistent storage. Afterwards, we clear the storage by calling the clear function. At last, we check if the entries still exist.

If you run this code you will see the following result in your terminal:

false
false

Simple Example Application

Now that we understand all the functionalities of the Shared Preferences plugin, let us create a simple implementation to demonstrate its general usage. The below example is a simple application that displays a title that is saved within the persistent storage of the device. The application also has two buttons that can be used to set the title and remove the title.

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  SharedPreferences? _preferences;

  @override
  void initState() {
    super.initState();
    _initializeSharedPreferences();
  }

  Future<void> _initializeSharedPreferences() async {
    _preferences = await SharedPreferences.getInstance();
  }

  void _setTitle() {
    _preferences?.setString('title', 'Only Flutter');
    setState(() {});
  }

  void _clearTitle() {
    _preferences?.remove('title');
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Center(
            child: Text(_preferences?.getString('title') ?? 'no title'),
          ),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: _setTitle,
                child: const Text('Set title'),
              ),
              ElevatedButton(
                onPressed: _clearTitle,
                child: const Text('Clear title'),
              )
            ],
          ),
        ),
      ),
    );
  }
}

In the above code snippet, we start by importing the Shared Preferences plugin. Afterward, we create a nullable _sharedPreferences variable. On this variable, we want to assign an instance of the SharedPreference class. Because we need this variable as soon as possible we call the _initializeSharedPreferences inside the initState function to ensure that it is assigned immediately.

In the build function we return a Scaffold widget with an AppBar widget. The AppBar widget takes a title and in this case, we want to retrieve this title from the persistent storage using the getString function. However, this value can be null, so we added a button to set the title.

To set the title we use the _setTitle function which calls the setString function and calls an empty setState function to rebuild the UI.

Other than that, we also have the _clearTitle function which can be called by clicking the second button. This will call the remove function and also calls an empty setState function.

If we run the application you can see that we can set and remove the title.

However, because we are using persistent storage you can restart your application after setting the title and you will see that the title will still be set.

Best Practices when Using the Shared Preferences Plugin

When using the Shared Preferences plugin, there are several best practices to enhance the management of entries in your application. These practices help prevent key mix-ups, accidental deletions of essential entries, and simplify plugin usage. I highly recommend checking out the following article: How to Manage the Shared Preferences in Flutter

Conclusion

The Shared Preferences package is very convenient to save simple data on the user’s device, like user preferences and settings. In this post, you have learned how to store, retrieve, and remove data from the device’s persistent storage.

Tijn van den Eijnde
Tijn van den Eijnde
Articles: 40

Leave a Reply

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