In Flutter, what is shared_preferences meant to store, which value types does it accept, and what should never go into it?
answer
- small, non-critical settings
- int, double, bool, String, List<String>
- NSUserDefaults, DataStore, localStorage
- plain text on disk
- writes are not guaranteed durable
basics
~20 sshared_preferences holds small, non-critical settings as int, double, bool, String or List<String>, backed by NSUserDefaults, Android DataStore or SharedPreferences, and localStorage on web. Never store secrets, large data or records you cannot afford to lose.
solid answer
~40 s`shared_preferences` wraps each platform's simple key-value store — `NSUserDefaults` on iOS and macOS, DataStore Preferences or Android `SharedPreferences`, `localStorage` on the web, a file on Linux and Windows. It accepts only `int`, `double`, `bool`, `String` and `List<String>`. It is right for things like the chosen language code or an "onboarding done" flag. It is wrong for tokens or passwords, because values are stored unencrypted — that is `flutter_secure_storage`'s job; wrong for lists of records or big JSON blobs, which belong in SQLite or Drift; and wrong for anything critical, because the package itself says a write is not guaranteed to be on disk when its Future completes.
code
dart · 20 linesimport 'package:shared_preferences/shared_preferences.dart';
class AppSettings {
AppSettings(this._prefs);
final SharedPreferencesAsync _prefs;
static const _languageKey = 'languageCode';
static const _onboardedKey = 'onboardingDone';
Future<String?> languageCode() => _prefs.getString(_languageKey);
Future<void> setLanguageCode(String code) =>
_prefs.setString(_languageKey, code);
Future<bool> onboardingDone() async =>
await _prefs.getBool(_onboardedKey) ?? false;
Future<void> markOnboarded() => _prefs.setBool(_onboardedKey, true);
}go deeper
Recall the five supported types and that the store is for small settings such as language or an onboarding flag, never tokens.
Explain the backing store per platform, nullable getters with defaults, TypeError on the wrong getter, and why durability is not guaranteed.
Show the judgement of placing each piece of app data — settings, secrets, records, blobs — in the right store and the cost of getting it wrong.
Frame storage choice as a data-classification policy for the team, so sensitive or critical data never lands in a convenience store by default.
## What the plugin is `shared_preferences` is a Flutter plugin maintained in the flutter/packages repository (2.5 at the time of writing). It does not implement storage itself: it forwards to whatever simple key-value store each platform already has, through a federated set of platform packages. | Platform | Backing store | |---|---| | iOS, macOS | `NSUserDefaults` | | Android | DataStore Preferences by default for the newer APIs; Android `SharedPreferences` for the legacy API or when configured | | Web | `localStorage` | | Linux | a file in the XDG data directory | | Windows | a file in the roaming AppData directory | ## The supported types The API accepts exactly five value types, each with its own getter and setter: - `int` — `setInt` / `getInt` - `double` — `setDouble` / `getDouble`; on platforms without doubles the value is stored as a float, so do not expect full precision back - `bool` — `setBool` / `getBool` - `String` — `setString` / `getString` - `List<String>` — `setStringList` / `getStringList` Getters return a nullable type and give `null` for a missing key, so a default is supplied at the call site: `await prefs.getBool('onboardingDone') ?? false`. Reading a key with the wrong typed getter throws a `TypeError`. There is no support for maps, dates or custom objects; you can serialise them into a `String`, but needing to do that is often a sign the data belongs elsewhere. ## What belongs there The sweet spot is small, user-level settings that are cheap to lose and easy to recompute: - the user's chosen language, for example `'pt'` - whether onboarding has been completed - theme mode, a units preference, a dismissed-tip flag - the last selected tab or filter If a value vanishes, the worst outcome should be that the user sees onboarding again or picks a language twice. ## What does not belong there 1. **Secrets.** Access tokens, refresh tokens, passwords and API keys. The stores above are not encrypted by the plugin; on a rooted or jailbroken device, in a backup, or in the browser's storage inspector, the values are readable. Secrets go to `flutter_secure_storage`, which uses the Keychain and Android's keystore-backed encryption. 2. **Large data.** The Flutter cookbook says the store is not designed for large amounts of data. The legacy and cached APIs load their keys into memory up front, and every value crosses a platform channel, so a megabyte of JSON costs you on every start. 3. **Records and relational data.** A list of saved forms, a message history or anything you query belongs in `sqflite` or Drift, with transactions and migrations. 4. **Critical data.** The README states that data may be persisted asynchronously and that a completed write is no guarantee it reached disk, so the plugin must not be used for critical data. A crash right after a write can lose it. 5. **Files and blobs.** Images or downloads go to files in an app directory, not into a `String` preference. ## A concrete example An app remembers the language the user picked and whether they finished onboarding. Both are tiny, both are recoverable, and both are read at startup: a textbook fit. Their sign-in token, by contrast, is a secret and goes into secure storage even though it is "just a string" — the type fits, the protection does not. ## How interviewers probe it The junior-level answer lists the types and says "settings". The follow-ups test judgement: why not the token (not encrypted), why not the cart or the offline form queue (records need a database), and what happens if the app is killed right after a write (it may not have persisted). Knowing the backing store per platform helps explain each answer rather than reciting it.
- Why not put the access token in shared_preferences if it is just a String?The type fits but the protection does not: the plugin stores values unencrypted in NSUserDefaults, DataStore or SharedPreferences files, or localStorage, where backups, rooted devices or dev tools can read them. Tokens belong in `flutter_secure_storage`, which uses the Keychain and keystore-backed encryption.
- What does getBool return for a key that was never written, and how do you handle it?It returns `null` — every getter is nullable, and on `SharedPreferencesAsync` it is a `Future<bool?>`. Supply the default at the call site, for example `await prefs.getBool('onboardingDone') ?? false`, so the absence of a value has one clear meaning.
shared_preferences is the sticky note on the fridge: perfect for "language: Portuguese" or "tour done", wrong for your bank PIN, and no place to keep the family photo album.
saying these in an interview costs you the question
- shared_preferences encrypts values, so it is fine for auth tokens.
- You can store any object; the plugin serialises maps automatically.
- Once setString's Future completes, the value is guaranteed to be on disk.
- It is a good place for the app's list of saved records.
- On iOS it writes to the Keychain.