Local Data Storage with SharedPreferences
7 questions foundWhat is the shared_preferences package, and what kinds of genuinely simple data is it actually genuinely well suited for storing directly on a user's own device?
Beginner The shared_preferences package provides a genuinely simple key value storage mechanism specifically for persisting small pieces of primitive data, such as strings, booleans, integers, and doubles, directly on a user's own device across separate individual app sessions, and it is genuinely best suited for storing simple app settings and preferences, such as a user's chosen theme, whether they have already seen a particular onboarding screen, or their preferred notification setting, rather than genuinely large amounts of structured data, which would generally be considerably better handled instead by a proper genuine database such as Hive or SQLite.
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('hasSeenOnboarding', true);
final hasSeenOnboarding = prefs.getBool('hasSeenOnboarding') ?? false;
Real-world example An app properly remembers whether a user has already genuinely seen its own initial onboarding screens using SharedPreferences, correctly avoiding showing that same onboarding flow again every single time that particular user actually reopens the app.
Common follow-ups: What genuinely happens to data stored using SharedPreferences if a user actually uninstalls the app?;What is the specific practical difference between SharedPreferences and Hive in terms of exactly what kind of data each one is genuinely best suited for?
Hive & NoSQL Local Storage;App Theming & Dark Mode
How do you actually properly read and write different basic primitive data types, such as a String, an int, and a bool, using the shared_preferences package's own available methods?
Beginner The shared_preferences package provides dedicated genuinely type specific methods for each basic supported primitive data type, including setString and getString for text values, setInt and getInt for whole numbers, and setBool and getBool for boolean true or false values, and each individual stored value is genuinely associated with its own particular unique specific string key, letting you reliably retrieve that exact same stored value again later simply by properly providing that exact same matching key string.
await prefs.setString('username', 'alex123');
await prefs.setInt('loginCount', 5);
final username = prefs.getString('username');
Real-world example A login screen stores a user's own chosen username using setString and properly tracks how many times they have already genuinely logged in using setInt, correctly retrieving both of those separately stored values again the very next time that same particular user actually reopens the app.
Common follow-ups: What genuinely happens if you try to actually read a specific key that was never actually properly saved in the first place?;Can SharedPreferences genuinely store a more complex object such as a List or a Map directly?
Dart Language Basics & Syntax;Dart Null Safety
How can you properly store a genuinely more complex object, such as a List of strings or a Map, using SharedPreferences, given that it only genuinely natively supports fairly simple basic primitive types directly?
Intermediate While SharedPreferences directly supports storing a List of strings natively through its own dedicated setStringList method, storing a genuinely more complex arbitrary object, such as a Map or your own genuinely fully custom Dart model class, typically requires first properly converting that particular object into a JSON formatted string using jsonEncode, storing that resulting string using the regular setString method, and then properly reversing that exact same process using jsonDecode when you later genuinely need to actually read that same particular stored value back again.
final userMap = {'name': 'Alex', 'age': 30};
await prefs.setString('userData', jsonEncode(userMap));
final decoded = jsonDecode(prefs.getString('userData')!);
Real-world example A settings screen stores a user's own several separate notification preferences together as one single combined Map by first properly converting it into a JSON string, correctly retrieving and properly decoding that exact same stored combined preferences object again later using jsonDecode.
Common follow-ups: At what specific point does storing genuinely increasingly complex JSON encoded data within SharedPreferences actually genuinely start to become a strong good sign you should instead be using a proper real database like Hive?;What genuinely happens to overall performance as you continue storing an increasing number of separate individual keys within SharedPreferences?
JSON Serialization & Deserialization;Hive & NoSQL Local Storage
How does SharedPreferences actually genuinely work differently underneath on Android compared to iOS, in terms of exactly what genuine underlying native storage mechanism each platform actually respectively uses?
Intermediate On Android, SharedPreferences is genuinely implemented directly on top of the platform's own native SharedPreferences API, which internally genuinely stores its own data as a simple XML file, while on iOS it is genuinely implemented instead on top of NSUserDefaults, Apple's own equivalent native platform mechanism specifically for storing this exact same kind of simple key value preference data, and while these genuine underlying native implementation details are generally kept properly and fully transparent from your own actual Flutter code, understanding this particular fact helps clarify precisely why this exact same package is genuinely well suited specifically for simple preferences data rather than genuinely large or particularly performance sensitive data.
// Flutter's shared_preferences package delegates to each platform's own
// respective native preference storage mechanism underneath
Real-world example A developer investigating why a specific stored preference value was not actually genuinely persisting correctly on one particular platform discovers a subtle platform specific difference in exactly how that native underlying storage mechanism actually properly handles a genuinely very large stored string value.
Common follow-ups: What genuinely practical size limits exist for a value actually stored using SharedPreferences on each individual respective platform?;How does this exact same underlying native storage difference genuinely relate to properly implementing a fully custom native platform channel instead?
Platform Channels (Native Android & iOS Integration);Flutter App Security Best Practices
Why should genuinely sensitive data, such as an authentication token or a password, never actually be stored using regular SharedPreferences, and what should genuinely be used instead?
Intermediate SharedPreferences stores its own underlying data completely unencrypted in plain readable text on both Android and iOS, meaning any genuinely sensitive data such as an authentication token, a password, or any other kind of personal identifying information could potentially be read directly by another malicious app on a genuinely compromised or rooted device, and the flutter_secure_storage package should genuinely be used instead for any kind of sensitive data, since it properly relies on each platform's own genuine native secure storage mechanism, specifically Keychain on iOS and an encrypted Keystore backed implementation on Android, providing genuinely considerably stronger security protection.
// Avoid this for sensitive data
await prefs.setString('authToken', token);
// Prefer this instead
await secureStorage.write(key: 'authToken', value: token);
Real-world example A banking app properly migrates its stored authentication token away from regular SharedPreferences over to flutter_secure_storage instead, meaningfully and considerably improving the genuine security protection of that particular sensitive piece of data.
Common follow-ups: What genuinely other specific kinds of data besides authentication tokens should also genuinely always be stored using secure storage instead?;What is the actual real practical performance difference between regular SharedPreferences and flutter_secure_storage?
Flutter App Security Best Practices;Firebase Authentication in Flutter
How can you properly and safely migrate existing data already stored using SharedPreferences over to a genuinely more full featured local database such as Hive, once an app's own particular storage needs have genuinely grown beyond what simple key value storage alone can reasonably handle?
Advanced Migrating away from SharedPreferences over to a genuinely more full featured solution like Hive typically requires writing a genuinely dedicated one time migration function that properly runs once during your app's own startup, reading each individual existing relevant SharedPreferences value, properly writing that exact same data over into its own genuinely correct new corresponding Hive box, and then finally properly clearing out that specific old now genuinely redundant SharedPreferences data, and genuinely carefully testing this particular migration path thoroughly against several genuinely realistic possible previous data states helps meaningfully ensure no existing user's own data is accidentally lost during that particular actual migration process.
final oldValue = prefs.getString('userData');
if (oldValue != null) {
final box = await Hive.openBox('userData');
await box.put('userData', jsonDecode(oldValue));
await prefs.remove('userData');
}
Real-world example A team migrating their app's own growing settings data away from SharedPreferences over to Hive writes a dedicated one time migration function that runs automatically during the app's own next startup after an update, correctly and safely preserving every single existing user's own previously already saved settings.
Common follow-ups: How do you actually properly and reliably track whether this particular one time migration has already genuinely completed successfully for a given specific user?;What genuinely happens if this particular migration process itself happens to actually fail partway through?
Hive & NoSQL Local Storage;Error Handling & Crash Reporting (Sentry & Crashlytics)
What are the practical performance considerations of reading from SharedPreferences repeatedly throughout your app, and how does properly caching those particular values in memory help meaningfully avoid genuinely unnecessary repeated disk access?
Advanced While a single individual SharedPreferences read operation is generally quite genuinely fast, repeatedly reading that exact same particular value many separate times throughout your app, such as checking a specific user preference on essentially every single individual widget rebuild, can still genuinely add up to meaningfully unnecessary overhead over time, and a genuinely common recommended optimization pattern involves properly loading all of your app's own relevant preferences once during startup, caching those particular values in memory using a dedicated settings or preferences service class, and then having the rest of your app genuinely read from that in memory cache instead, only actually writing back to SharedPreferences itself whenever a given specific value genuinely actually changes.
class SettingsService {
bool _darkMode = false;
bool get darkMode => _darkMode;
Future<void> loadSettings() async {
final prefs = await SharedPreferences.getInstance();
_darkMode = prefs.getBool('darkMode') ?? false;
}
}
Real-world example A large app caches all of its own user preferences in memory once during startup through a dedicated SettingsService, meaningfully avoiding genuinely unnecessary repeated SharedPreferences disk reads scattered throughout dozens of separate individual widgets across the entire app.
Common follow-ups: What genuinely happens if the underlying SharedPreferences value is actually changed from somewhere else outside of your own particular in memory cached service?;How does this exact same specific caching pattern genuinely relate to properly using a dedicated state management solution like Provider instead?
State Management with Provider;Flutter Performance Optimization