DEV Community

kanta13jp1
kanta13jp1

Posted on

Flutter Local Storage Guide — SharedPreferences vs Hive vs SQLite

Flutter Local Storage Guide — SharedPreferences vs Hive vs SQLite

Choosing the right local storage solution in Flutter directly impacts performance and code simplicity. Here's a practical breakdown of the three main options.

SharedPreferences — Key-Value Settings

Best for small primitives: user settings, theme, login state.

dependencies:
  shared_preferences: ^2.3.0
Enter fullscreen mode Exit fullscreen mode
class SettingsService {
  static const _themeKey = 'theme_mode';

  Future<void> saveTheme(String theme) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(_themeKey, theme);
  }

  Future<String> getTheme() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString(_themeKey) ?? 'system';
  }
}
Enter fullscreen mode Exit fullscreen mode

Hive — Fast NoSQL Storage

Perfect for structured data, offline caching, and large datasets. TypeAdapters keep it type-safe.

dependencies:
  hive: ^2.2.3
  hive_flutter: ^1.1.0
dev_dependencies:
  hive_generator: ^2.0.1
  build_runner: ^2.4.0
Enter fullscreen mode Exit fullscreen mode
@HiveType(typeId: 0)
class Task extends HiveObject {
  @HiveField(0) late String id;
  @HiveField(1) late String title;
  @HiveField(2) late bool isDone;
  @HiveField(3) late DateTime createdAt;
}
Enter fullscreen mode Exit fullscreen mode
// Generate adapter: dart run build_runner build

await Hive.initFlutter();
Hive.registerAdapter(TaskAdapter());
final box = await Hive.openBox<Task>('tasks');

// CRUD
await box.put(task.id, task);
final all = box.values.toList();
await box.delete(task.id);
Enter fullscreen mode Exit fullscreen mode

Reactive UI with ValueListenableBuilder

ValueListenableBuilder<Box<Task>>(
  valueListenable: Hive.box<Task>('tasks').listenable(),
  builder: (context, box, _) {
    final tasks = box.values.toList();
    return ListView.builder(
      itemCount: tasks.length,
      itemBuilder: (_, i) => TaskTile(task: tasks[i]),
    );
  },
)
Enter fullscreen mode Exit fullscreen mode

SQLite (sqflite) — Relational Data

Use when you need complex queries, JOINs, or aggregations.

class DatabaseHelper {
  static Database? _db;

  static Future<Database> get database async {
    _db ??= await _initDb();
    return _db!;
  }

  static Future<Database> _initDb() async {
    final path = join(await getDatabasesPath(), 'app.db');
    return openDatabase(
      path,
      version: 1,
      onCreate: (db, _) async {
        await db.execute('''
          CREATE TABLE tasks (
            id TEXT PRIMARY KEY,
            title TEXT NOT NULL,
            is_done INTEGER DEFAULT 0,
            created_at TEXT NOT NULL
          )
        ''');
      },
    );
  }

  static Future<void> insert(Map<String, dynamic> task) async {
    final db = await database;
    await db.insert('tasks', task, conflictAlgorithm: ConflictAlgorithm.replace);
  }

  static Future<List<Map<String, dynamic>>> getAll() async {
    final db = await database;
    return db.query('tasks', orderBy: 'created_at DESC');
  }
}
Enter fullscreen mode Exit fullscreen mode

Decision Matrix

Use case Best choice
App settings / flags SharedPreferences
Offline cache / large datasets Hive
Complex relations / filtering sqflite
Encryption required Hive AES / sqlcipher

How I Use This in My App

In my life management app (自分株式会社), I use Hive for offline journal storage and manage Supabase sync timing with Riverpod. Local-first design means the app is fully functional even on airplane mode.


Building something offline-capable in Flutter? Drop a comment — curious what storage stack you're using.

Top comments (0)