Store key-value data on disk
If you have a relatively small collection of key-values
to save, you can use the shared_preferences
plugin.
Normally,
you would have to write native platform integrations for storing
data on both iOS and Android. Fortunately,
the shared_preferences
plugin can be used to persist
key-value data on disk. The shared preferences plugin
wraps NSUserDefaults
on iOS and SharedPreferences
on Android,
providing a persistent store for simple data.
This recipe uses the following steps:
- Add the dependency.
- Save data.
- Read data.
- Remove data.
1. Add the dependency
Before starting, add the shared_preferences
plugin to the pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
shared_preferences: "<newest version>"
2. Save data
To persist data, use the setter methods provided by the
SharedPreferences
class. Setter methods are available for
various primitive types, such as setInt
, setBool
, and setString
.
Setter methods do two things: First, synchronously update the key-value pair in-memory. Then, persist the data to disk.
// obtain shared preferences
final prefs = await SharedPreferences.getInstance();
// set value
prefs.setInt('counter', counter);
3. Read data
To read data, use the appropriate getter method provided by the
SharedPreferences
class. For each setter there is a corresponding getter.
For example, you can use the getInt
, getBool
, and getString
methods.
final prefs = await SharedPreferences.getInstance();
// Try reading data from the counter key. If it doesn't exist, return 0.
final counter = prefs.getInt('counter') ?? 0;
4. Remove data
To delete data, use the remove()
method.
final prefs = await SharedPreferences.getInstance();
prefs.remove('counter');
Supported types
Although key-value storage is easy and convenient to use, it has limitations:
- Only primitive types can be used:
int
,double
,bool
,string
, andstringList
. - It’s not designed to store a lot of data.
For more information about shared preferences on Android, see the shared preferences documentation on the Android developers website.
Testing support
It’s a good idea to test code that persists data using
shared_preferences
. You can do this by mocking out the
MethodChannel
used by the shared_preferences
library.
Populate SharedPreferences
with initial values in your tests
by running the following code in a setupAll()
method in
your test files:
const MethodChannel('plugins.flutter.io/shared_preferences')
.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'getAll') {
return <String, dynamic>{}; // set initial values here if desired
}
return null;
});
Complete example
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); // This widget is the root of the application. @override Widget build(BuildContext context) { return const MaterialApp( title: 'Shared preferences demo', home: MyHomePage(title: 'Shared preferences demo'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; @override void initState() { super.initState(); _loadCounter(); } //Loading counter value on start void _loadCounter() async { final prefs = await SharedPreferences.getInstance(); setState(() { _counter = (prefs.getInt('counter') ?? 0); }); } //Incrementing counter after click void _incrementCounter() async { final prefs = await SharedPreferences.getInstance(); setState(() { _counter = (prefs.getInt('counter') ?? 0) + 1; prefs.setInt('counter', _counter); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }