The local storage is saving structured data inside the user’s device.
SharedPreferences is like a sticky note: great for jotting down a phone number, terrible for writing a novel.
If your App needs:
- Save thousands of records.
- Perform complex searches (e.g., “Users over 18 living in Madrid”).
- Relate data (Orders belonging to a Customer).
You need a database. In Flutter, we can choose between relational solutions, key-value stores, and object-oriented databases. Let’s compare two well-known approaches.
SQLite and Relational Databases
SQLite is the mobile industry standard. Android and iOS come with it pre-installed. It is a Relational (SQL) database.
In Flutter, we use the sqflite package.
How does it work?
Think of Excel spreadsheets (Tables) that relate to each other. You have to define a strict schema (CREATE TABLE…) and write queries in SQL language.
- Standard: If you know SQL, you know how to use it.
- Relationships: Ideal if your data is highly connected (Tables with Foreign Keys).
- Robust: It has been used for decades and offers transactions.
- More boilerplate code: With
sqflite, you need to open the database, create tables, and map data. - Rigid: If you change a field in your model, you have to perform a database “Migration.”
- Only supports basic types: You cannot save a
List<String>directly; you must convert it.
// Conceptual example of sqflite
// 1. Write raw SQL (Prone to syntax errors)
await db.execute(
'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)'
);
// 2. Insert maps
await db.insert('Test', {'name': 'my name', 'value': 123});
// 3. Query
List<Map> list = await db.rawQuery('SELECT * FROM Test WHERE value > ?', [100]);
If you want typed queries, migrations, and cross-platform support, Drift provides a reactive layer on top of SQLite. sqflite is still useful when you prefer to work with the API and SQL more directly.
Hive as a Key-Value Store
Hive is a NoSQL key-value store written in Dart. Its API resembles a persistent Map and is convenient for data queried by key.
How does it work?
Think of a “Box”. Inside the box, you can put whatever you want, assigning it a key. It’s like a persistent and supercharged Map<String, dynamic>.
- Direct access: Can offer very good performance for key-based operations, depending on the volume and device.
- Simplicity: No SQL or relational tables.
- Dart objects: Supports basic types, lists, and maps; you need to register adapters for custom classes.
- Cross-platform: Has implementations for mobile, web, and desktop.
- No relational queries: Doesn’t offer
JOINand ad-hoc SQL queries; you must design indexes and access patterns differently. - Schema evolution: Even without SQL tables, changes to objects and adapters also require planning and testing.
// Conceptual example of Hive
// 1. Open a box
var box = await Hive.openBox('testBox');
// 2. Save data (Looks like a Map)
await box.put('name', 'David');
await box.put('friends', ['Ana', 'Juan', 'Pedro']); // Saves lists directly
// 3. Read
var name = box.get('name');
Comparison: Which one to choose?
This is the decision guide we use in real projects:
| Feature | SQLite (sqflite / drift) | Hive |
|---|---|---|
| Type | Relational (Tables) | NoSQL (Objects/Boxes) |
| Learning curve | Requires SQL and migrations | Simple for key-based access |
| Performance | Depends on queries, indexes, and platform | Depends on access pattern and volume |
| Relationships | Powerful (JOINs) | Manual |
| Ideal use case | Structured data, relationships, and varied queries. | Extended preferences, cache, and simple key-based access. |