FLUTTER: Flutter Data Persistence

Flutter apps need to store data locally for offline access, user preferences, and caching. This covers all the main persistence options — Shared Preferences for key-value storage, SQLite for structured data, Hive for fast NoSQL storage, and direct file management — along with CRUD patterns and serialization.

Every meaningful app needs to remember something between sessions. Whether it is a user's theme preference, a cached list of items, or a full relational dataset, Flutter provides a clear set of tools for every persistence need.


Choosing the Right Storage Solution

md
What kind of data do you need to store?
         |
         +-- Simple key-value pairs (settings, flags)
         |         --> Shared Preferences
         |
         +-- Structured relational data (tables, queries)
         |         --> SQLite (sqflite)
         |
         +-- Fast, lightweight NoSQL data
         |         --> Hive
         |
         +-- Files (images, documents, downloads)
         |         --> dart:io file system
         |
         +-- Client-side web storage
                   --> IndexedDB (web)


Shared Preferences

Shared Preferences stores simple key-value pairs persistently on the device. It maps to SharedPreferences on Android and NSUserDefaults on iOS.

Best suited for:

  • User settings (theme, language, notification preferences)
  • App configuration flags
  • Small amounts of primitive data (strings, booleans, integers, doubles)

bash
flutter pub add shared_preferences

dart
import 'package:shared_preferences/shared_preferences.dart';

class SettingsService {
  // Save data
  static Future<void> saveThemeMode(bool isDark) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('dark_mode', isDark);
  }

  // Read data
  static Future<bool> loadThemeMode() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getBool('dark_mode') ?? false;
  }

  // Delete data
  static Future<void> clearAll() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.clear();
  }
}

Shared Preferences supports the following types:

MethodType
setString / getStringString
setInt / getIntint
setDouble / getDoubledouble
setBool / getBoolbool
setStringList / getStringListList<String>

SQLite (sqflite)

SQLite is an embedded relational database. Use it for complex data that requires queries, joins, filtering, and ordering.

bash
flutter pub add sqflite
flutter pub add path

dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
  static Database? _database;

  static Future<Database> getDatabase() async {
    if (_database != null) return _database!;

    final path = join(await getDatabasesPath(), 'app_database.db');
    _database = await openDatabase(
      path,
      version: 1,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE students (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            firstName TEXT NOT NULL,
            lastName TEXT NOT NULL,
            age INTEGER,
            major TEXT
          )
        ''');
      },
    );
    return _database!;
  }

  // CREATE
  static Future<int> insertStudent(Map<String, dynamic> student) async {
    final db = await getDatabase();
    return await db.insert('students', student);
  }

  // READ
  static Future<List<Map<String, dynamic>>> getAllStudents() async {
    final db = await getDatabase();
    return await db.query('students', orderBy: 'lastName ASC');
  }

  // UPDATE
  static Future<int> updateStudent(Map<String, dynamic> student) async {
    final db = await getDatabase();
    return await db.update(
      'students',
      student,
      where: 'id = ?',
      whereArgs: [student['id']],
    );
  }

  // DELETE
  static Future<int> deleteStudent(int id) async {
    final db = await getDatabase();
    return await db.delete('students', where: 'id = ?', whereArgs: [id]);
  }
}


Hive

Hive is a lightweight, fast NoSQL database written entirely in Dart. It stores data in a type-safe, binary format and is significantly faster than SQLite for simple read/write operations.

bash
flutter pub add hive
flutter pub add hive_flutter

dart
import 'package:hive_flutter/hive_flutter.dart';

// Initialize Hive
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Hive.initFlutter();
  await Hive.openBox('settings');
  runApp(const MyApp());
}

// Use the box
final box = Hive.box('settings');

// Write
box.put('username', 'amr_tarek');
box.put('score', 9850);

// Read
final username = box.get('username', defaultValue: 'Guest');
final score = box.get('score', defaultValue: 0);

// Delete
box.delete('username');

For storing custom Dart objects, Hive uses TypeAdapters (generated with hive_generator).


Direct File Management

For downloading and saving media files, documents, or any binary data, use the dart:io file system directly.

bash
flutter pub add path_provider

dart
import 'dart:io';
import 'package:path_provider/path_provider.dart';

class FileService {
  // Get the app's documents directory
  static Future<Directory> getDocumentsDir() async {
    return await getApplicationDocumentsDirectory();
  }

  // Write a text file
  static Future<File> writeFile(String filename, String content) async {
    final dir = await getDocumentsDir();
    final file = File('${dir.path}/$filename');
    return await file.writeAsString(content);
  }

  // Read a text file
  static Future<String> readFile(String filename) async {
    final dir = await getDocumentsDir();
    final file = File('${dir.path}/$filename');
    if (await file.exists()) {
      return await file.readAsString();
    }
    return '';
  }

  // Delete a file
  static Future<void> deleteFile(String filename) async {
    final dir = await getDocumentsDir();
    final file = File('${dir.path}/$filename');
    if (await file.exists()) {
      await file.delete();
    }
  }
}


Serialization and Deserialization

When working with databases or APIs, you need to convert between Dart objects and JSON/Map formats.

Defining a Model with Serialization

dart
class Student {
  final int? id;
  final String firstName;
  final String lastName;
  final int age;
  final String major;

  Student({
    this.id,
    required this.firstName,
    required this.lastName,
    required this.age,
    required this.major,
  });

  // Deserialize from a Map (e.g., from SQLite or JSON API)
  factory Student.fromJson(Map<String, dynamic> json) {
    return Student(
      id: json['id'],
      firstName: json['firstName'],
      lastName: json['lastName'],
      age: json['age'],
      major: json['major'],
    );
  }

  // Serialize to a Map (e.g., for SQLite insert or JSON encoding)
  Map<String, dynamic> toJson() {
    return {
      if (id != null) 'id': id,
      'firstName': firstName,
      'lastName': lastName,
      'age': age,
      'major': major,
    };
  }
}

Using dart:convert for JSON:

dart
import 'dart:convert';

// Decode JSON string to Dart Map
final Map<String, dynamic> userMap = jsonDecode(jsonString);
final user = Student.fromJson(userMap);

// Encode Dart object to JSON string
final String encoded = jsonEncode(user.toJson());


CRUD with State Management

Combine persistence with Provider for a complete data management layer.

dart
import 'package:flutter/foundation.dart';

class StudentProvider with ChangeNotifier {
  List<Student> _students = [];

  List<Student> get students => List.unmodifiable(_students);

  void addStudent(Student student) {
    _students.add(student);
    _saveToStorage();
    notifyListeners();
  }

  void removeStudent(int id) {
    _students.removeWhere((s) => s.id == id);
    _saveToStorage();
    notifyListeners();
  }

  void updateStudent(Student updated) {
    final index = _students.indexWhere((s) => s.id == updated.id);
    if (index != -1) {
      _students[index] = updated;
      _saveToStorage();
      notifyListeners();
    }
  }

  void _saveToStorage() {
    // Persist to Shared Preferences, SQLite, or Hive
  }
}


Storage Comparison

SolutionTypeBest ForSpeed
Shared PreferencesKey-valueSettings, flagsFast
SQLite (sqflite)RelationalComplex structured dataMedium
HiveNoSQLObjects, fast readsVery fast
FilesBinary/TextMedia, documentsVariable
IndexedDBKey-valueWeb client storageFast

The right choice depends on your data shape, query requirements, and performance needs. Many apps use a combination — Shared Preferences for settings, Hive or SQLite for domain data, and the file system for media.