Learn how to persist data temporarily in Flutter applications using Provider for state management and shared_preferences for local storage, ensuring consistent app state during and beyond sessions.
In the realm of mobile app development, ensuring that data persists across different states of an application is crucial for providing a seamless user experience. This section delves into the concept of persisting data temporarily in Flutter applications, focusing on maintaining state during app sessions and optionally extending persistence beyond sessions using local storage solutions like shared_preferences
.
Data persistence refers to the ability of an application to retain data across different states and sessions. In the context of our To-Do List App, persisting data temporarily means ensuring that the list of tasks remains visible and intact during the app’s runtime. This is essential for user experience, as it allows users to continue their tasks without losing progress when navigating through the app or when the app is temporarily closed.
Provider is a popular state management solution in Flutter that helps manage and propagate state changes throughout the app. By using Provider, we can ensure that our task list remains consistent during the app’s runtime.
Provider allows us to manage the state of our application efficiently. Here’s how we can use it to manage our task list:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class TaskProvider extends ChangeNotifier {
List<Task> _tasks = [];
List<Task> get tasks => _tasks;
void addTask(String description) {
_tasks.add(Task(description: description));
notifyListeners();
}
void deleteTask(int index) {
_tasks.removeAt(index);
notifyListeners();
}
void updateTask(int index, String newDescription) {
_tasks[index].description = newDescription;
notifyListeners();
}
void toggleTaskCompletion(int index) {
_tasks[index].isCompleted = !_tasks[index].isCompleted;
notifyListeners();
}
}
class Task {
String description;
bool isCompleted;
Task({required this.description, this.isCompleted = false});
}
TaskProvider
class extends ChangeNotifier
, allowing it to notify listeners whenever the state changes.addTask
, deleteTask
, updateTask
, and toggleTaskCompletion
modify the task list and trigger UI updates.While Provider helps manage state during the app’s runtime, shared_preferences
can be used to persist data beyond the app session. This means that even if the app is closed and reopened, the data remains intact.
To use shared_preferences
, we need to add it to our project dependencies.
Add to pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
provider: ^6.0.5
shared_preferences: ^2.0.15
Run flutter pub get
to fetch the package.
To save tasks locally, we need to serialize our task data into a format that can be stored in shared_preferences
, such as JSON.
Code Example:
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
class TaskProvider extends ChangeNotifier {
List<Task> _tasks = [];
List<Task> get tasks => _tasks;
TaskProvider() {
_loadTasks();
}
void addTask(String description) {
_tasks.add(Task(description: description));
notifyListeners();
_saveTasks();
}
void deleteTask(int index) {
_tasks.removeAt(index);
notifyListeners();
_saveTasks();
}
void updateTask(int index, String newDescription) {
_tasks[index].description = newDescription;
notifyListeners();
_saveTasks();
}
void toggleTaskCompletion(int index) {
_tasks[index].isCompleted = !_tasks[index].isCompleted;
notifyListeners();
_saveTasks();
}
Future<void> _saveTasks() async {
final prefs = await SharedPreferences.getInstance();
List<String> taskList = _tasks.map((task) => jsonEncode(task.toJson())).toList();
await prefs.setStringList('tasks', taskList);
}
Future<void> _loadTasks() async {
final prefs = await SharedPreferences.getInstance();
List<String>? taskList = prefs.getStringList('tasks');
if (taskList != null) {
_tasks = taskList.map((task) => Task.fromJson(jsonDecode(task))).toList();
notifyListeners();
}
}
}
class Task {
String description;
bool isCompleted;
Task({required this.description, this.isCompleted = false});
Map<String, dynamic> toJson() => {
'description': description,
'isCompleted': isCompleted,
};
factory Task.fromJson(Map<String, dynamic> json) {
return Task(
description: json['description'],
isCompleted: json['isCompleted'],
);
}
}
Task
class includes toJson
and fromJson
methods to convert task objects to and from JSON format._saveTasks
method converts the list of tasks to a list of JSON strings and stores it in shared_preferences
._loadTasks
method retrieves the stored tasks from shared_preferences
and deserializes them back into Task
objects when the provider is initialized.To better understand the flow of data persistence from state management to local storage, consider the following diagram:
flowchart LR A[Persisting Data Temporarily] --> B[Provider State] A --> C[Local Storage] B --> B1[State Consistency] C --> C1[shared_preferences] C1 --> C2[Save Tasks] C1 --> C3[Load Tasks] B --> D[Notify Listeners] C2 --> D C3 --> D
shared_preferences
to save and load tasks, extending persistence beyond sessions.Persisting data temporarily is a crucial aspect of app development that enhances user experience by maintaining state consistency. By leveraging Provider for state management and shared_preferences
for local storage, developers can ensure that their Flutter applications provide a seamless and reliable user experience. As you implement these techniques, remember to follow best practices and consider the specific needs of your application to choose the most appropriate data persistence strategy.