Flutter logo

SharedPreferences is a module bringing the functionality of Android’s eponymous API to your cross-platform Flutter apps. It uses Apple’s similar NSUserDefaults API when running on iOS and macOS. Implementations are also included for Linux and Windows so it’ll run everywhere Flutter apps can.

In this guide, we’ll show how you can use SharedPreferences to persist simple settings within your app. The module is best used for small pieces of non-essential state such as user preferences, saved bookmarks, and cached config values sent by a server.

Adding SharedPreferences

Add the SharedPreferences module to your app using the Flutter CLI’s pub add command:

flutter pub add shared_preferences

Reference the library in your code by importing it at the top of your Dart files:

import 'package:shared_preferences/shared_preferences.dart';

You’ll be able to access the SharedPreferences API within files that include this import statement.

Adding Data to SharedPreferences

To begin using SharedPreferences you must first get an instance of the SharedPreferences class that’s connected to your app’s on-disk config file. Call the getInstance() static method to load and parse the file into a SharedPreferences instance. This is an asynchronous method so use the await keyword inside an async function to streamline access.

void main() async {
    var prefs = await SharedPreferences.getInstance();
}

Now you can use the methods of the prefs instance to store and retrieve your data. SharedPreferences works with key-value pairs. It supports five data types: int, String, bool, double, and List<String>. Values are added to the store using setter methods; each method takes a key name as its first parameter and a value as its second:

prefs.setBool("darkTheme", true);

To work with other types of value, call their corresponding setter methods:

prefs.setString("theme", "dark");
 
prefs.setInt("sessionId", 1000);
 
prefs.setDouble("lastTemperature", 18.5);
 
prefs.setStringList("featureFlags", ["darkTheme", "redesign"]);

The setters don’t accept null as a value. Use the separate remove() method to delete an item from the store:

prefs.remove("sessionId");

To delete all the stored settings, call the clear() method. Clears are asynchronous operations which resolve when the on-disk settings file has been removed.

await prefs.clear();

Complex Types

You can’t natively persist complex types such as objects, maps, and non-string lists. To store one of these, you could encode it as JSON and use the setString() method:

import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
 
void main () async {
    var bookmarkedArticles = [10, 15, 18];
    var prefs = await SharedPreferences.getInstance();
    prefs.setString("bookmarks", jsonEncode(bookmarkedArticles));
}

Call jsonDecode() when you retrieve the value with getString() (see below) to convert it back to an appropriate Dart type.

Retrieving Persisted Data

Values are retrieved using getter methods. Like with the setters, there’s a unique getter for each supported data type. This means you can safely use the return values without having to manually cast between types.

var theme = prefs.getString("theme");
// "dark"
 
var sessionId = prefs.getInt("sessionId");
// 1000
 
var lastTemperature = prefs.getDouble("lastTemperature");
// 18.5

It’s important you use the getter that matches the data type of the stored value. If the key matches but it has a different data type, an exception will be thrown.

Null is returned when the key doesn’t exist in the saved data. You can check whether an item’s in the store in advance by calling containsKey():

if (prefs.containsKey("theme")) {
    // ...
}

You can also enumerate all the keys in the store:

var keys = pref.getKeys();

This is useful when you need to know what’s currently saved on-disk so you can run appropriate migrations after an app update.

Caveats and Limitations

SharedPreferences is easy to work with when you’re storing simple key-value pairs. However you shouldn’t rely on it for critical data or security-sensitive values.

Platform-specific implementations vary but you should assume your data will be stored as plain-text in a potentially user-accessible location. Encrypting values before you save them can add more protection but will still leave the keys exposed.

SharedPreferences is an asynchronous API. While the getter and setter methods look like they’re synchronous, that doesn’t extend all the way down to the disk level. Disk writes may be asynchronous so closing the app immediately after setting a value could cause it to be lost.

It’s best to choose a different API when you’re storing large amounts of data, writing complex values, or in need of a more dependable operating model with resilient error handling. You could use the Files API to write a custom config file to a restricted location or create a SQLite database for your app. The latter approach is ideal when you’ll want to perform advanced queries against the stored data.

Writing Unit Tests

SharedPreferences includes a utility to help you write unit tests without a real preferences store. The static setMockInitialValues() method lets you inject values that will populate the instance returned by getInstance():

void main() async {
 
    Map<String, Object> values = <String, Object>{"foo": "bar"};
    SharedPreferences.setMockInitialValues(values);
 
    var instance = await SharedPreferences.getInstance();
    var foo = instance.getString("foo");
    // bar
 
}

You can add settings to your app while maintaining your ability to unit test each component. Testing the code that retrieves your settings lets you guarantee the app will behave correctly in each scenario.

Summary

SharedPreferences is a convenient way to persist simple state inside your Flutter app. It focuses on the storage and retrieval of key-value pairs using the most common data types. SharedPreferences is best used for non-essential settings where a failed write won’t negatively impact your application.

It can be effective to leverage SharedPreferences in a similar manner to cookies in web applications. Basic counters, boolean values, and config keys make sense in SharedPreferences; everything else belongs in a well-defined data structure that you can manage more precisely.

Profile Photo for James Walker James Walker
James Walker is a contributor to How-To Geek DevOps. He is the founder of Heron Web, a UK-based digital agency providing bespoke software development services to SMEs. He has experience managing complete end-to-end web development workflows, using technologies including Linux, GitLab, Docker, and Kubernetes.
Read Full Bio »