Dart Tutorial

Flutter Lesson 50 of 83 3 min read

shared_preferences in Flutter: Saving Simple Data

Save small values in Flutter with shared_preferences: settings, flags and tokens, plus secure storage for sensitive data.

On this page

shared_preferences stores small pieces of data as key and value pairs that survive restarts. It suits settings and flags: the chosen theme, whether the user has seen the welcome screen, the last selected tab.

flutter pub add shared_preferences

Reading and writing #

import 'package:shared_preferences/shared_preferences.dart';

Future<void> example() async {
  final prefs = await SharedPreferences.getInstance();

  // Write
  await prefs.setInt('launchCount', 5);
  await prefs.setBool('darkMode', true);
  await prefs.setString('userName', 'Asha');
  await prefs.setDouble('fontScale', 1.2);
  await prefs.setStringList('recentSearches', ['shoes', 'bag']);

  // Read: null if the key was never set, so give a default
  final count = prefs.getInt('launchCount') ?? 0;
  final dark = prefs.getBool('darkMode') ?? false;
  final name = prefs.getString('userName');

  // Remove
  await prefs.remove('userName');
  await prefs.clear(); // everything
}

Five types are supported: int, double, bool, String and List<String>.

A counter that remembers #

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

void main() => runApp(const MaterialApp(home: CounterPage()));

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

  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  static const _key = 'counter';
  int _count = 0;

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

  Future<void> _load() async {
    final prefs = await SharedPreferences.getInstance();
    if (!mounted) return;
    setState(() => _count = prefs.getInt(_key) ?? 0);
  }

  Future<void> _increment() async {
    setState(() => _count++);
    final prefs = await SharedPreferences.getInstance();
    await prefs.setInt(_key, _count);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Saved counter')),
      body: Center(child: Text('$_count', style: const TextStyle(fontSize: 48))),
      floatingActionButton: FloatingActionButton(
        onPressed: _increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Close the app completely and open it again. The number is still there.

Wrap it in a class #

Key strings scattered through the app invite typos. Keep them in one place.

class SettingsStore {
  SettingsStore(this._prefs);
  final SharedPreferences _prefs;

  static const _themeKey = 'themeMode';
  static const _onboardedKey = 'onboarded';

  ThemeMode get themeMode =>
      ThemeMode.values.byName(_prefs.getString(_themeKey) ?? 'system');

  Future<void> setThemeMode(ThemeMode mode) => _prefs.setString(_themeKey, mode.name);

  bool get hasOnboarded => _prefs.getBool(_onboardedKey) ?? false;

  Future<void> completeOnboarding() => _prefs.setBool(_onboardedKey, true);
}

Load the preferences before the app starts, so the first frame already has the right theme.

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized(); // required before using plugins in main
  final prefs = await SharedPreferences.getInstance();
  runApp(MyApp(settings: SettingsStore(prefs)));
}

Storing an object #

Encode it as JSON text.

import 'dart:convert';

Future<void> saveUser(SharedPreferences prefs, User user) =>
    prefs.setString('user', jsonEncode(user.toJson()));

User? loadUser(SharedPreferences prefs) {
  final text = prefs.getString('user');
  if (text == null) return null;
  return User.fromJson(jsonDecode(text) as Map<String, dynamic>);
}

This is fine for one small object. For lists of records, use a database.

The newer API #

The package also provides SharedPreferencesAsync and SharedPreferencesWithCache, which the maintainers recommend for new code. The method names are the same idea (setInt, getInt), with reads that return futures in the async version. Everything in this lesson carries over.

What not to store here #

DataUse instead
Passwords, tokens, API keysflutter_secure_storage
Lists of records you search or sortSQLite or another database
Images and large filesFiles, with the path stored in a database
Data that must sync between devicesA backend such as Firebase

Preferences are stored as plain text on the device. They are not encrypted.

Secure storage #

flutter_secure_storage keeps values in the iOS Keychain and the Android Keystore.

flutter pub add flutter_secure_storage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

const storage = FlutterSecureStorage();

await storage.write(key: 'authToken', value: token);
final token = await storage.read(key: 'authToken'); // null if absent
await storage.delete(key: 'authToken');

Use it for anything that would cause harm if read from a lost or compromised phone.

Try it yourself #

Add a settings screen with a dark mode switch, a font size slider and a “user name” field. Save each with shared_preferences, and load them before runApp so the app opens with the saved values.

Practise in the playground Updated by Santosh Adhikari