Flutter Local Storage SharedPrefs
Not all data needs a server. User preferences, login tokens, dark mode settings, and onboarding completion flags belong on the device. Flutter provides two common ways to store data locally: SharedPreferences for simple key-value data, and SQLite for structured data.
Types of Local Storage
┌──────────────────────────────────────────────────┐ │ LOCAL STORAGE OPTIONS │ ├─────────────────────┬────────────────────────────┤ │ SharedPreferences │ Simple key → value pairs │ │ │ Best for: settings, tokens │ ├─────────────────────┼────────────────────────────┤ │ SQLite (sqflite) │ Full relational database │ │ │ Best for: lists, records │ ├─────────────────────┼────────────────────────────┤ │ Hive │ Fast NoSQL database │ │ │ Best for: objects, offline │ └─────────────────────┴────────────────────────────┘
SharedPreferences — Simple Key-Value Storage
SharedPreferences stores small pieces of data as key-value pairs, similar to a settings file on the device.
Step 1 — Add the Package
In pubspec.yaml:
─────────────────
dependencies:
shared_preferences: ^2.2.3
Run: flutter pub get
Step 2 — Save Data
import 'package:shared_preferences/shared_preferences.dart';
Future<void> saveUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('username', 'Ravi');
await prefs.setInt('age', 28);
await prefs.setBool('darkMode', true);
await prefs.setDouble('rating', 4.5);
print('Data saved!');
}
Step 3 — Read Data
Future<void> readUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? username = prefs.getString('username'); // 'Ravi'
int? age = prefs.getInt('age'); // 28
bool? darkMode = prefs.getBool('darkMode'); // true
print('Username: $username, Age: $age, Dark: $darkMode');
}
Step 4 — Delete Data
Future<void> clearData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove('username'); // Remove one key
await prefs.clear(); // Remove all keys
}
Real-World Example — Remember Login State
Many apps skip the login screen if the user is already signed in. SharedPreferences stores the login state between sessions.
class AuthService {
static const String _loginKey = 'isLoggedIn';
static const String _tokenKey = 'authToken';
// Save login state after successful login
Future<void> saveLoginState(String token) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_loginKey, true);
await prefs.setString(_tokenKey, token);
}
// Check login state on app start
Future<bool> isLoggedIn() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_loginKey) ?? false;
}
// Clear on logout
Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
}
}
App Launch Flow:
─────────────────────────────────────────────
App Starts
↓
Check isLoggedIn()
↓
┌───────────────────────────────────────┐
│ true → go to HomeScreen │
│ false → go to LoginScreen │
└───────────────────────────────────────┘
Dark Mode Preference — Live Example
class ThemeSettings extends StatefulWidget {
@override
State<ThemeSettings> createState() => _ThemeSettingsState();
}
class _ThemeSettingsState extends State<ThemeSettings> {
bool _isDark = false;
@override
void initState() {
super.initState();
_loadTheme();
}
Future<void> _loadTheme() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_isDark = prefs.getBool('darkMode') ?? false;
});
}
Future<void> _toggleTheme(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('darkMode', value);
setState(() { _isDark = value; });
}
@override
Widget build(BuildContext context) {
return ListTile(
title: Text('Dark Mode'),
trailing: Switch(
value: _isDark,
onChanged: _toggleTheme,
),
);
}
}
SQLite with sqflite — For Structured Data
When you need to store a list of records (tasks, contacts, products), use sqflite. It gives you a full SQL database on the device.
Add the Package
dependencies:
sqflite: ^2.3.3
path: ^1.9.0
Create and Use a Database
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static Database? _db;
Future<Database> get database async {
if (_db != null) return _db!;
_db = await _initDB();
return _db!;
}
Future<Database> _initDB() async {
String path = join(await getDatabasesPath(), 'tasks.db');
return openDatabase(
path,
version: 1,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
isDone INTEGER
)
''');
},
);
}
Future<void> insertTask(String title) async {
final db = await database;
await db.insert('tasks', {'title': title, 'isDone': 0});
}
Future<List<Map<String, dynamic>>> getTasks() async {
final db = await database;
return db.query('tasks');
}
Future<void> deleteTask(int id) async {
final db = await database;
await db.delete('tasks', where: 'id = ?', whereArgs: [id]);
}
}
Choosing the Right Storage
| Data Type | Best Storage Option |
|---|---|
| Settings, flags, tokens | SharedPreferences |
| Lists of structured records | sqflite (SQLite) |
| Dart objects, fast access | Hive |
| Large files (images, PDFs) | path_provider + File |
