SharedPreferences
Storing simple key-value pairs persistently in Flutter — the easiest way to save user preferences and app settings.
SharedPreferences is a Flutter plugin that provides a simple way to store key-value pairs persistently across app sessions. It wraps platform-specific persistent storage:
✅ When to Use SharedPreferences
- Storing user preferences (theme, language, notifications)
- Saving app settings (sound, volume, font size)
- Storing simple user data (username, email, high score)
- Session data (login state, last visit)
⚠️ Not for Large Data
SharedPreferences is designed for small amounts of simple data . Do not use it for storing large lists, images, or complex objects. For large data, use Hive or SQLite .
SharedPreferences supports the following data types:
📦 Primitive Types
-
int– Integer values -
double– Decimal values -
bool– True/false values -
String– Text values
📋 Collection Types
-
List<String>– Lists of strings
Note: For lists of other types, use JSON serialization.
// Primitive types
await prefs.setInt(
'counter'
,
10
);
await prefs.setDouble(
'price'
,
19.99
);
await prefs.setBool(
'isDarkMode'
,
true
);
await prefs.setString(
'username'
,
'john_doe'
);
// Collection types
await prefs.setStringList(
'favorites'
, [
'apple'
,
'banana'
,
'cherry'
]);
To use SharedPreferences, you need to add the dependency and import the package.
dependencies:
shared_preferences: ^2.5.5
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PreferencesService {
// Get an instance of SharedPreferences
static Future<SharedPreferences> getPrefs() async {
return await SharedPreferences.getInstance();
}
// ----- WRITE DATA -----
static Future<void> setThemeMode(bool isDark) async {
final prefs = await getPrefs();
await prefs.setBool('dark_mode', isDark);
}
static Future<void> setUsername(String name) async {
final prefs = await getPrefs();
await prefs.setString('username', name);
}
static Future<void> setCounter(int count) async {
final prefs = await getPrefs();
await prefs.setInt('counter', count);
}
// ----- READ DATA -----
static Future<bool> getThemeMode() async {
final prefs = await getPrefs();
return prefs.getBool('dark_mode') ?? false;
}
static Future<String> getUsername() async {
final prefs = await getPrefs();
return prefs.getString('username') ?? 'Guest';
}
static Future<int> getCounter() async {
final prefs = await getPrefs();
return prefs.getInt('counter') ?? 0;
}
// ----- DELETE DATA -----
static Future<void> removeUsername() async {
final prefs = await getPrefs();
await prefs.remove('username');
}
// ----- CLEAR ALL -----
static Future<void> clearAll() async {
final prefs = await getPrefs();
await prefs.clear();
}
}
Here's a complete example of a settings app that uses SharedPreferences to persist user preferences.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Settings App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: SettingsScreen(),
);
}
}
class SettingsScreen extends StatefulWidget {
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
bool _isDarkMode = false;
bool _notificationsEnabled = true;
bool _soundEnabled = true;
String _username = 'Guest';
int _fontSize = 16;
bool _isLoading = true;
final TextEditingController _usernameController = TextEditingController();
@override
void initState() {
super.initState();
_loadSettings();
}
@override
void dispose() {
_usernameController.dispose();
super.dispose();
}
Future<void> _loadSettings() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_isDarkMode = prefs.getBool('dark_mode') ?? false;
_notificationsEnabled = prefs.getBool('notifications') ?? true;
_soundEnabled = prefs.getBool('sound') ?? true;
_username = prefs.getString('username') ?? 'Guest';
_fontSize = prefs.getInt('font_size') ?? 16;
_usernameController.text = _username;
_isLoading = false;
});
}
Future<void> _saveSetting(String key, dynamic value) async {
final prefs = await SharedPreferences.getInstance();
if (value is bool) {
await prefs.setBool(key, value);
} else if (value is int) {
await prefs.setInt(key, value);
} else if (value is String) {
await prefs.setString(key, value);
}
}
Future<void> _saveUsername() async {
final newUsername = _usernameController.text.trim();
if (newUsername.isNotEmpty) {
await _saveSetting('username', newUsername);
setState(() {
_username = newUsername;
});
}
}
Future<void> _resetSettings() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
await _loadSettings();
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return Scaffold(
appBar: AppBar(title: Text('Settings')),
body: Center(child: CircularProgressIndicator()),
);
}
return Scaffold(
appBar: AppBar(
title: Text('⚙️ Settings'),
centerTitle: true,
actions: [
IconButton(
icon: Icon(Icons.refresh),
onPressed: _loadSettings,
tooltip: 'Reload',
),
],
),
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
children: [
// User Profile Section
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'👤 Profile',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12),
Row(
children: [
CircleAvatar(
radius: 30,
child: Text(
_username.isNotEmpty
? _username[0].toUpperCase()
: '?',
style: TextStyle(fontSize: 20),
),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_username,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
'User since 2024',
style: TextStyle(color: Colors.grey.shade500),
),
],
),
),
],
),
SizedBox(height: 8),
Row(
children: [
Expanded(
child: TextField(
controller: _usernameController,
decoration: InputDecoration(
hintText: 'Change username',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
),
),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: _saveUsername,
child: Text('Save'),
),
],
),
],
),
),
),
SizedBox(height: 16),
// Appearance Section
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'🎨 Appearance',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
SwitchListTile(
title: Text('Dark Mode'),
subtitle: Text('Enable dark theme'),
value: _isDarkMode,
onChanged: (value) async {
setState(() => _isDarkMode = value);
await _saveSetting('dark_mode', value);
},
),
ListTile(
title: Text('Font Size'),
subtitle: Text('${_fontSize}px'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.remove),
onPressed: () async {
if (_fontSize > 12) {
final newSize = _fontSize - 2;
setState(() => _fontSize = newSize);
await _saveSetting('font_size', newSize);
}
},
),
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
if (_fontSize < 28) {
final newSize = _fontSize + 2;
setState(() => _fontSize = newSize);
await _saveSetting('font_size', newSize);
}
},
),
],
),
),
],
),
),
),
SizedBox(height: 16),
// Preferences Section
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'🔔 Preferences',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
SwitchListTile(
title: Text('Notifications'),
subtitle: Text('Receive push notifications'),
value: _notificationsEnabled,
onChanged: (value) async {
setState(() => _notificationsEnabled = value);
await _saveSetting('notifications', value);
},
),
SwitchListTile(
title: Text('Sound Effects'),
subtitle: Text('Play sounds on interactions'),
value: _soundEnabled,
onChanged: (value) async {
setState(() => _soundEnabled = value);
await _saveSetting('sound', value);
},
),
],
),
),
),
SizedBox(height: 16),
// Danger Zone
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'⚠️ Danger Zone',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Reset All Settings'),
content: Text(
'This will reset all your settings to default values. Are you sure?',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Cancel'),
),
ElevatedButton(
onPressed: () {
_resetSettings();
Navigator.pop(context);
},
child: Text('Reset'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
),
],
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red.shade50,
foregroundColor: Colors.red,
),
child: Text('Reset All Settings'),
),
),
],
),
),
),
],
),
),
);
}
}
shared_preferences: ^2.5.5
to your
pubspec.yaml
.
SharedPreferences.getInstance()
to get a reference to the preferences.
setXxx()
methods to store data (e.g.,
setBool('key', value)
).
getXxx()
methods to retrieve data with fallback values.
remove('key')
to delete a single key or
clear()
to delete all.
✅ Do's
-
Use
meaningful key names
(e.g.,
'user_theme_preference') -
Always provide
fallback values
when reading (
?? defaultValue) - Group related preferences with consistent prefixes
- Consider using a service class to encapsulate all SharedPreferences logic
-
Call
reload()if preferences are modified outside the app
❌ Don'ts
- Don't store large objects or large lists
- Don't store sensitive data (use encryption for passwords)
- Don't use SharedPreferences for critical data (writes are asynchronous)
- Don't use SharedPreferences as a database for large datasets
SharedPreferences.getInstance()
and all
setXxx()
methods are asynchronous.
Always use
await
or handle the
Future
properly.
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('key', value);
If a key doesn't exist,
getXxx()
returns
null
.
Not handling this can cause null errors.
final bool isDark = prefs.getBool('dark_mode') ?? false;
SharedPreferences doesn't support custom objects. Trying to store them directly will cause errors.
Convert objects to JSON strings using
jsonEncode()
and parse with
jsonDecode()
.
🎯 Key Takeaway
SharedPreferences is the simplest way to store small amounts of key-value data in Flutter. It's perfect for user preferences, app settings, and simple state that needs to persist across app sessions. Always provide fallback values and use a service class to keep your code clean.