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.