Learn how to persist data across sessions in Flutter using SharedPreferences, local databases, and secure storage. Explore best practices and practical examples to enhance your app's data management capabilities.
In mobile app development, persisting data across sessions is crucial for providing a seamless user experience. Whether it’s saving user preferences, caching data for offline access, or storing sensitive information securely, Flutter offers several tools and techniques to manage data persistence effectively. This section will explore various methods for persisting data in Flutter applications, including using SharedPreferences for simple data, local databases for complex data, and secure storage for sensitive information. We’ll also discuss best practices and provide practical examples to help you implement these techniques in your projects.
SharedPreferences is a simple key-value storage solution that allows you to store small amounts of data persistently. It’s ideal for saving user preferences, settings, or any other lightweight data that needs to be retained across app sessions.
SharedPreferences stores data in a persistent key-value store, making it easy to save and retrieve simple data types such as integers, strings, and booleans. Here’s a basic example of how to use SharedPreferences in a Flutter application:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class CounterApp extends StatefulWidget {
@override
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0;
@override
void initState() {
super.initState();
_loadCounter();
}
// Load counter value from SharedPreferences
_loadCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_counter = prefs.getInt('counter') ?? 0;
});
}
// Increment counter and save to SharedPreferences
_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_counter++;
prefs.setInt('counter', _counter);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
In this example, the counter value is stored in SharedPreferences each time the user increments it. When the app is restarted, the counter value is loaded from SharedPreferences, ensuring that the user’s progress is not lost.
For more complex data storage needs, such as storing large datasets or structured data, local databases are a better choice. Flutter provides several packages for local database management, including sqflite
and hive
.
sqflite
for SQL-Based Storagesqflite
is a popular package for SQLite database management in Flutter. It allows you to perform SQL operations to manage complex data structures efficiently.
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static final _databaseName = "MyDatabase.db";
static final _databaseVersion = 1;
static final table = 'my_table';
static final columnId = '_id';
static final columnName = 'name';
// Singleton instance
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
// Database reference
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
_initDatabase() async {
String path = join(await getDatabasesPath(), _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT NOT NULL
)
''');
}
Future<int> insert(Map<String, dynamic> row) async {
Database db = await instance.database;
return await db.insert(table, row);
}
Future<List<Map<String, dynamic>>> queryAllRows() async {
Database db = await instance.database;
return await db.query(table);
}
}
In this example, a simple SQLite database is set up with a single table. You can extend this to include more tables and complex queries as needed.
hive
for NoSQL-Based Storagehive
is a lightweight and fast NoSQL database solution for Flutter. It’s particularly well-suited for storing structured data without the overhead of SQL.
import 'package:hive/hive.dart';
void main() async {
await Hive.initFlutter();
var box = await Hive.openBox('myBox');
// Store data
box.put('name', 'John Doe');
// Retrieve data
var name = box.get('name');
print('Name: $name');
}
hive
is easy to set up and use, making it a great choice for applications that require fast and efficient data storage.
When dealing with sensitive information, such as user credentials or tokens, it’s important to use secure storage solutions. flutter_secure_storage
provides a secure way to store sensitive data by encrypting it.
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = FlutterSecureStorage();
// Write value
await storage.write(key: 'username', value: 'john_doe');
// Read value
String? username = await storage.read(key: 'username');
This package ensures that sensitive data is stored securely, protecting it from unauthorized access.
To better understand how data flows from the app to persistent storage and back, consider the following diagram:
graph TD; A[Flutter App] --> B{SharedPreferences}; A --> C{Local Database}; A --> D{Secure Storage}; B --> E[Store/Retrieve Simple Data]; C --> F[Store/Retrieve Complex Data]; D --> G[Store/Retrieve Sensitive Data];
This diagram illustrates the different pathways data can take depending on the storage solution used.
As an exercise, try implementing a feature that saves user preferences between sessions. For example, you could create a settings page where users can toggle dark mode, and save this preference using SharedPreferences. When the app is restarted, the user’s preference should be loaded and applied automatically.
Persisting data across sessions is a fundamental aspect of mobile app development. By using the right tools and techniques, you can ensure that your app provides a seamless and consistent user experience. Whether you’re storing simple preferences or complex datasets, Flutter offers a variety of solutions to meet your needs. Remember to follow best practices to optimize performance and security, and encourage hands-on experimentation to deepen your understanding.