As we build increasingly complex Flutter applications, there will come a time when you need to store data locally on the user's device. This is crucial for providing a seamless user experience, allowing data to persist even when the app is closed and reopened. Flutter offers excellent solutions for this, broadly categorized into simple preferences and more robust local databases.
When you need to store small amounts of simple data like user settings, themes, or flags, shared_preferences is your go-to package. It provides a straightforward way to save and retrieve primitive data types (strings, integers, booleans, doubles, and lists of strings) using a key-value pair system.
First, add the shared_preferences package to your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.0.15Then, import the package into your Dart file:
import 'package:shared_preferences/shared_preferences';Here's how you can save a boolean value (e.g., for dark mode preference):
Future<void> saveDarkModePreference(bool isDarkMode) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('darkMode', isDarkMode);
}And here's how to retrieve that value:
Future<bool> getDarkModePreference() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool('darkMode') ?? false; // Default to false if not set
}You can also save and retrieve other data types similarly, using methods like setString(), setInt(), setDouble(), and setStringList().
When your application needs to store larger amounts of structured data, or data with relationships between different entities, a local database becomes a necessity. Flutter offers several excellent options for local database solutions, the most popular being SQLite. SQLite is a lightweight, serverless, self-contained, transactional SQL database engine. It's widely used and well-supported.
For working with SQLite in Flutter, the sqflite package is the standard choice. It provides a low-level interface to SQLite databases. For a more object-relational mapping (ORM) approach, which abstracts away much of the SQL, consider packages like drift (formerly moor) or isar.
Let's look at a simplified example using sqflite. First, add the package:
dependencies:
flutter:
sdk: flutter
sqflite: ^2.0.0+4
path: ^1.8.0You'll also need the path package to help locate the database file.
Here's a basic illustration of opening a database and creating a table:
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
Future<Database> initializeDatabase() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'my_app.db');
return await openDatabase(
path,
version: 1,
onCreate: (db, version) async {
await db.execute(
'CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)'
);
},
);
}This code snippet demonstrates initializing the database. If the database file doesn't exist, it will be created. The onCreate callback is where you define your table schema. After initialization, you can perform standard SQL operations like INSERT, SELECT, UPDATE, and DELETE.
The decision between shared_preferences and a local database depends on the complexity and volume of data you need to store. For simple settings and flags, shared_preferences is efficient and easy to use. For any structured data, relationships, or larger datasets, a dedicated local database like SQLite (accessed via sqflite or ORMs like drift or isar) is the more appropriate and scalable solution.
graph TD
A[App Needs Data Storage] --> B{Data Complexity & Volume?};
B -- Simple & Small --> C[Shared Preferences];
B -- Structured & Large --> D[Local Database];
D --> E[SQLite via sqflite];
D --> F[ORM like drift or isar];