Introduction to Local Data Storage in Flutter
Introduction to Local Data Storage in Flutter
Almost every real-world Flutter application needs to persist data between sessions. Whether you are saving user preferences, caching API responses, storing a shopping cart, or managing a local database of records, local data storage is a foundational skill. Flutter does not have a single built-in storage system — instead, it provides a rich ecosystem of packages suited to different data shapes, sizes, and performance requirements.
In this lesson you will survey the full landscape of local storage options available to Flutter developers, understand the trade-offs between them, and learn how to pick the right tool for any given requirement.
The Main Local Storage Options
Flutter developers typically choose from six categories of local storage:
- SharedPreferences — lightweight key-value pairs for simple settings
- SQLite (via sqflite) — a full relational database engine for structured data
- Hive — a fast NoSQL key-value / box store with optional type adapters
- Drift (formerly Moor) — a type-safe reactive SQLite wrapper with a Dart DSL
- File / Path Provider — raw file I/O for arbitrary binary or text data
- Flutter Secure Storage — encrypted key-value storage backed by the platform keychain
SharedPreferences
SharedPreferences (package: shared_preferences) wraps Android's SharedPreferences and Apple's NSUserDefaults. It stores a flat map of primitives (bool, int, double, String, List<String>) serialised to disk as XML or a property list.
SharedPreferences — Read and Write
import 'package:shared_preferences/shared_preferences.dart';
Future<void> saveTheme(String theme) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('theme', theme);
}
Future<String> loadTheme() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('theme') ?? 'light';
}
Best for: user preferences (theme, language, onboarding flags, last-seen version). Avoid storing large objects, collections, or sensitive data here — it is not encrypted and not designed for querying.
SQLite via sqflite
The sqflite package embeds the SQLite engine directly in the app bundle. You write raw SQL or use helper methods to manage tables, indexes, and transactions. It is the right choice when your data is inherently relational — think contacts, tasks, or financial records with foreign-key relationships.
sqflite — Open a Database and Insert a Row
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
Future<Database> openAppDatabase() async {
final dbPath = await getDatabasesPath();
return openDatabase(
join(dbPath, 'app.db'),
version: 1,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
done INTEGER NOT NULL DEFAULT 0
)
''');
},
);
}
Future<void> insertTask(Database db, String title) async {
await db.insert('tasks', {'title': title, 'done': 0});
}
Best for: structured relational data, complex queries with JOIN or GROUP BY, data that needs indexing for fast lookup. Trade-off: you write raw SQL strings (error-prone) and manage migrations manually.
Hive
Hive (package: hive_flutter) is a pure-Dart, lightweight NoSQL store. Data lives in typed boxes (think collections). With TypeAdapters you can serialise your own Dart classes directly. Hive is considerably faster than SQLite for simple reads and writes because it operates as an in-memory store backed by binary files — no SQL parsing overhead.
Best for: object caching, offline-first apps, preferences that are richer than primitives. Trade-off: no relational queries; joining data across boxes is done in Dart code.
Drift (Formerly Moor)
Drift sits on top of SQLite but replaces raw SQL strings with a type-safe Dart DSL generated at build time. Changes to your schema are caught at compile time, not at runtime. It also supports reactive queries — a Stream that emits a new list every time the underlying table changes — which integrates naturally with Flutter's StreamBuilder.
Best for: medium-to-large apps that need the power of SQLite but want compile-time safety and reactivity. Trade-off: adds code-generation to your build pipeline (build_runner).
File / Path Provider
For data that does not fit a key-value or tabular model — images, PDFs, audio recordings, JSON exports — you write directly to the filesystem using dart:io and resolve directories with path_provider. The package exposes platform-correct locations such as the documents directory and the temporary cache directory.
Best for: binary files, large text blobs, user-generated content, app-generated reports. Trade-off: you own all serialisation, versioning, and cleanup logic.
Flutter Secure Storage
Flutter Secure Storage (package: flutter_secure_storage) stores key-value pairs encrypted with AES on Android (via EncryptedSharedPreferences or the Android Keystore) and stored in the iOS/macOS Keychain. Values are never written to disk in plain text.
Best for: tokens, passwords, PINs, API keys, refresh tokens — anything that would be a security incident if extracted from the device. Trade-off: slower than SharedPreferences; only handles String values.
Choosing the Right Storage Option
Use this decision guide when selecting a storage backend:
- Simple settings (bool, int, String)? → SharedPreferences
- Sensitive credentials or tokens? → Flutter Secure Storage
- Relational data with SQL queries? Need compile-time safety? → Drift
- Relational data, comfortable with raw SQL? → sqflite
- Object graph, need speed, no complex queries? → Hive
- Binary files, images, arbitrary blobs? → File + path_provider
Comparison at a Glance
The table below summarises the key dimensions across all six options:
| Option | Data Model | Encrypted | Reactive | Complexity |
|---|---|---|---|---|
| SharedPreferences | Key-Value (primitives) | No | No | Low |
| Secure Storage | Key-Value (String) | Yes | No | Low |
| Hive | Key-Value / Objects | Optional | ValueListenable | Medium |
| sqflite | Relational (SQL) | No | No | Medium |
| Drift | Relational (Dart DSL) | No | Stream | Medium-High |
| File / path_provider | Raw bytes / text | Manual | No | Varies |
Summary
Flutter's local storage ecosystem gives you a tool for every scenario. SharedPreferences handles simple settings with one line of code. Flutter Secure Storage locks down sensitive values using platform keychains. Hive delivers fast, schemaless object persistence. sqflite brings the full power of SQL to your device. Drift adds type safety and reactivity on top of SQLite. path_provider + dart:io covers any file-based need. In the upcoming lessons you will implement each option with real code and learn the patterns used in production Flutter applications.