Skip to content

Intermediate · Networking, storage and Firebase · topic 33 of 50 · 30 min

Local storage

Explanation

Choose storage by the kind of data. shared_preferences stores small key-value settings (theme, language, onboarding seen), never secrets. flutter_secure_storage keeps tokens in the platform's secure storage (Keychain on iOS, encrypted storage on Android). For structured data like orders or notes, use SQLite through sqflite or the drift package. path_provider gives the right folders for files.

Why store data on the device?

Apps should remember things: the user's language, whether they finished onboarding, a draft message, a cached product list for offline use, the login token. Storing them locally makes apps faster (no waiting for the network), usable offline, and friendlier. Different kinds of data need different tools.

DataToolNotes
Small settings and flagsshared_preferencesSimple key-value; not encrypted
Tokens, PINs, secretsflutter_secure_storageUses iOS Keychain / Android secure storage
Lists of records with queriessqflite (SQLite) or driftTables, SQL queries, transactions
Files: images, PDFs, exportspath_provider + dart:ioApp documents or cache directories

shared_preferences

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

Future<void> saveLanguage(String code) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('language', code);
}

Future<String> loadLanguage() async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString('language') ?? 'en';          // default when nothing is saved
}
Supports String, int, double, bool and List<String>. Newer versions also offer SharedPreferencesAsync; check the package page.
Careful: shared_preferences is stored in plain files on the device. Never store passwords, tokens or personal documents there. Use flutter_secure_storage for secrets.

All these storage calls are asynchronous because disk access is slow compared to memory. Load settings early (for example before runApp, or in a splash screen) and keep them in your state management layer, so widgets do not wait for disk reads every time they build.

Secure storage for tokens

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

const storage = FlutterSecureStorage();

Future<void> saveToken(String token) => storage.write(key: 'auth_token', value: token);
Future<String?> readToken() => storage.read(key: 'auth_token');
Future<void> logout() => storage.delete(key: 'auth_token');
Values are encrypted using the platform's secure storage.

SQLite for structured data

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

Future<Database> openNotesDb() async {
  return openDatabase(
    join(await getDatabasesPath(), 'notes.db'),
    version: 1,
    onCreate: (db, version) => db.execute(
      'CREATE TABLE notes(id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT NOT NULL, created INTEGER NOT NULL)',
    ),
  );
}

Future<void> addNote(Database db, String text) =>
    db.insert('notes', {'text': text, 'created': DateTime.now().millisecondsSinceEpoch});

Future<List<Map<String, Object?>>> latestNotes(Database db) =>
    db.query('notes', orderBy: 'created DESC', limit: 20);
sqflite works on Android, iOS and macOS. Use whereArgs for values to avoid SQL injection.

drift is a popular alternative built on SQLite that generates type-safe Dart code from your tables and queries, supports streams that update when data changes, and works on more platforms. It needs build_runner. For small apps sqflite is fine; for larger data layers many teams prefer drift.

On the web, sqflite is not available; drift or IndexedDB-based packages are options there.

Files with path_provider

path_provider gives platform-correct folders: getApplicationDocumentsDirectory() for files the user expects to keep, and getTemporaryDirectory() for caches the system may delete. Combine it with dart:io File to read and write (not available on the web).

Think of it like this: Choosing storage is like organising a home. Small notes go on the fridge door (shared_preferences), jewellery goes in the locker (secure storage), the household accounts go in a proper register with columns (SQLite), and photo albums go on the bookshelf (files).

Common mistakes

  • Storing a login token in shared_preferences.
  • Building SQL with string concatenation from user input: SQL injection risk. Use whereArgs.
  • Opening the database again on every query. Open it once and reuse it.
  • Forgetting that data must be migrated when the table structure changes (increase version and use onUpgrade).

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Local storage · 1/6

Settings | Secrets | Records | Files

Choose storage by the kind of data.

Prefer a video by a teacher? Find free videos on “flutter local storage shared_preferences sqflite secure storage drift” on YouTube ↗

In real life: A notes app that works offline

A student notes app stores notes in SQLite, the chosen theme and font size in shared_preferences, and the cloud-sync login token in secure storage. Notes open instantly in a metro tunnel; the theme is applied before the first frame; and if the phone is lost, the token is not readable from plain files.

Practice (0/3 done)

Task 1 · Question

Where should a login token be stored?

Task 2 · Question

Which is the safe way to query a note by id in sqflite?

Task 3 · Practice

Build a tiny expense app: save expenses (title, amount, date) in SQLite with sqflite, show them newest first in a ListView, remember the chosen currency symbol in shared_preferences, and show the monthly total with a SQL SUM query.

Quick quiz

Question 1

1.Is shared_preferences encrypted?

Question 2

2.Which package suits a list of 5,000 transactions with queries?

Question 3

3.Which folder may the operating system clear when space is low?

Question 4

4.What should you do when changing a SQLite table in a released app?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Is shared_preferences encrypted?

Answer: No; it is for non-sensitive settings

Use secure storage for secrets.

Which package suits a list of 5,000 transactions with queries?

Answer: sqflite or drift (SQLite)

Databases handle structured data.

Which folder may the operating system clear when space is low?

Answer: Temporary (cache) directory

Use it only for caches.

What should you do when changing a SQLite table in a released app?

Answer: Increase the version and migrate in onUpgrade

Existing users keep their data.

Who secures the physical data centre buildings?Related

Answer: The cloud provider

Physical security is always the provider's part.

From The shared responsibility model

Where should a login token be stored?Related

Answer: Encrypted storage backed by the Keystore

Secrets need encryption.

From Offline-first apps and DataStore

A company's storage bucket of invoices was left public and was downloaded by strangers. Whose responsibility was that setting?Related

Answer: The customer's

Bucket access settings are configuration, which is security IN the cloud.

From The shared responsibility model

Which storage loses its data when the instance is stopped?Related

Answer: Instance store

Instance store disks are physically attached to the host and are wiped on stop or terminate.

From Block and file storage: EBS, instance store and EFS

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Networking, storage and Firebase” before its exam: Firebase in Flutter.
  2. Module 8 exam: Networking, storage and Firebase · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Animations and performance. 50 topics left in the course before the final exam.

← Previous: JSON models