How to persist data locally in Flutter

Using SharedPreferences, Hive, sqflite, and Drift for local data persistence in Flutter

, updated

Flutter offers several local persistence options ranging from simple key-value stores to full relational databases.

SharedPreferences — simple key-value storage

Best for: settings, flags, small strings, tokens.

dependencies:
  shared_preferences: ^2.2.0
import 'package:shared_preferences/shared_preferences.dart';

// Write
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', 'abc123');
await prefs.setBool('dark_mode', true);
await prefs.setInt('launch_count', 5);

// Read
final token = prefs.getString('token') ?? '';
final darkMode = prefs.getBool('dark_mode') ?? false;
final launches = prefs.getInt('launch_count') ?? 0;

// Remove
await prefs.remove('token');

// Clear all
await prefs.clear();

Hive — fast NoSQL database

Best for: structured data without needing SQL. Very fast, works on all platforms.

dependencies:
  hive: ^2.2.0
  hive_flutter: ^1.1.0
import 'package:hive_flutter/hive_flutter.dart';

// Initialize
await Hive.initFlutter();

// Open a box
final box = await Hive.openBox('settings');

// Write
await box.put('theme', 'dark');
await box.put('fontSize', 16);

// Read
final theme = box.get('theme', defaultValue: 'light');
final fontSize = box.get('fontSize', defaultValue: 14);

// Custom objects — register adapter
@HiveType(typeId: 0)
class User extends HiveObject {
  @HiveField(0)
  String name;

  @HiveField(1)
  int age;

  User({required this.name, required this.age});
}

// After generating adapter:
// flutter packages pub run build_runner build
await Hive.openBox<User>('users');
await box.add(User(name: 'Alice', age: 30));

sqflite — SQLite database

Best for: relational data, complex queries, structured schemas.

dependencies:
  sqflite: ^2.3.0
  path: ^1.9.0
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

Future<Database> initDb() async {
  final dbPath = await getDatabasesPath();
  return openDatabase(
    join(dbPath, 'my_app.db'),
    version: 1,
    onCreate: (db, version) async {
      await db.execute('''
        CREATE TABLE users (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          name TEXT NOT NULL,
          email TEXT
        )
      ''');
    },
  );
}

// Insert
Future<void> insertUser(Database db, User user) async {
  await db.insert('users', user.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace);
}

// Query
Future<List<User>> getUsers(Database db) async {
  final maps = await db.query('users', orderBy: 'name ASC');
  return maps.map((m) => User.fromMap(m)).toList();
}

// Update
Future<void> updateUser(Database db, User user) async {
  await db.update('users', user.toMap(), where: 'id = ?', whereArgs: [user.id]);
}

// Delete
Future<void> deleteUser(Database db, int id) async {
  await db.delete('users', where: 'id = ?', whereArgs: [id]);
}

Drift (formerly Moor) provides type-safe access to SQLite with compile-time query verification.

dependencies:
  drift: ^2.15.0
  sqlite3_flutter_libs: ^0.5.0

dev_dependencies:
  drift_dev: ^2.15.0
  build_runner: ^2.4.0
import 'package:drift/drift.dart';
import 'package:drift/native.dart';

// Define tables
class Users extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get name => text()();
  TextColumn get email => text().nullable()();
}

// Define database
@DriftDatabase(tables: [Users])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(NativeDatabase.memory());

  @override
  int get schemaVersion => 1;

  // Queries
  Future<List<User>> getAllUsers() => select(users).get();
  Stream<List<User>> watchAllUsers() => select(users).watch();
  Future<void> insertUser(UsersCompanion user) => into(users).insert(user);
  Future<void> updateUser(User user) => update(users).replace(user);
  Future<void> deleteUser(int id) =>
      (delete(users)..where((u) => u.id.equals(id))).go();
}

Run code generation: dart run build_runner build.

Which should you use?

Solution Best for Complexity Relational
SharedPreferences Settings, tokens, flags Low No
Hive Structured non-relational data Medium No
sqflite SQL queries, relational data Medium Yes
Drift Type-safe SQLite, production apps Higher Yes