Dans cette histoire, je vais vous raconter mon expérience de l’implémentation d’une base de données hors ligne dans mon projet Flutter.
Pour ajouter SQLite dans votre application, vous devez installer le package sqflite. C’est une bibliothèque tierce qui contient SQLite.
Pour installer SQFlite, je tape dans le terminal :
flutter pub add sqflite
Ecrire cela dans le terminal est bien plus rapide que de rechercher la dernière version de sqflite sur le site pub.dev et de l’ajouter au fichier pubspec.yaml. Dans votre fichier pubspec.yaml, une nouvelle ligne contenant sqflite devrait être automatiquement ajoutée dans les dépendances, cela ressemble à quelque chose comme ceci :
dependecies:
flutter:
sdk: flutter
sqflite: ^2.0.0+3
Helper de la base de données
La première chose à faire est de créer une classe qui puisse gérer la création et les appels à la base de données. J’ai créé report_database_helper.dart en tant que singleton afin de pouvoir accéder à la base de données n’importe où et de ne pas occuper trop de mémoire.
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class ReportDBHelper {
ReportDBHelper._constructor();
static const tableName = 'report';
static const columnId = '_id';
static const columnProjectId = 'project_id';
static const columnActivityType = 'activity_type';
static const columnActivityName = 'activity_name';
static const columnExecutionMethod = 'execution_method';
static const columnMonitoringDate = 'monitoring_date';
static const columnMonitoringPeriod = 'monitoring_period';
static const columnActivityStage = 'activity_stage';
static const columnConclusion = 'conclusion';
static final ReportDBHelper instance = ReportDBHelper._constructor();
static const _dbName = 'myDatabase.db';
static const _dbVersion = 1;
Database? _database;
Future<Database> get database async {
if (_database != null) {
return _database!;
}
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
String directory = await getDatabasesPath();
String path = join(directory, _dbName);
return openDatabase(
path,
version: _dbVersion,
onCreate: _onCreate,
onConfigure: _onConfigure,
);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $tableName (
$columnId INTEGER PRIMARY KEY AUTOINCREMENT,
$columnProjectId INTEGER NOT NULL,
$columnActivityType TEXT NOT NULL,
$columnActivityName TEXT NOT NULL,
$columnExecutionMethod TEXT NOT NULL,
$columnMonitoringDate INTEGER NOT NULL,
$columnMonitoringPeriod TEXT NOT NULL,
$columnActivityStage TEXT NOT NULL,
$columnConclusion TEXT NOT NULL)
''');
}
Future _onConfigure(Database db) async {
await db.execute('PRAGMA foreign_keys = ON');
}
Future<int> insert(Map<String, dynamic> row) async {
Database db = await instance.database;
return db.insert(tableName, row);
}
Future<List<Map<String, dynamic>>> queryAll() async {
Database db = await instance.database;
return db.query(tableName);
}
Future<Map<String, dynamic>> queryById(int id) async {
Database db = await instance.database;
List<Map<String, dynamic>> results =
await db.query(tableName, where: '$columnId = ?', whereArgs: [id]);
return results.single;
}
Future<int> update(Map<String, dynamic> row) async {
Database db = await instance.database;
return db.update(
tableName,
row,
where: '$columnId = ?',
whereArgs: [row[columnId]],
);
}
Future<int> delete(int id) async {
Database db = await instance.database;
return db.delete(tableName, where: '$columnId = ?', whereArgs: [id]);
}
}
Dans SQLite, il n’y a pas de type de colonne qui puisse stocker une date. Si vous voulez stocker une date comme date_de_surveillance, vous devez utiliser INTEGER et convertir votre date en entier avec DateTime.fromMillisecondsSinceEpoch(int) et à partir d’un entier avec DateTime.millisecondsSinceEpoch . Vous pouvez voir cela un peu plus bas.
Il s’agit du modèle de la classe Report que je souhaite stocker dans la base de données. Vous pouvez ignorer les attributs de type Liste, car pour stocker une liste, il faut une relation de type un à plusieurs dans nos tables, ce qui n’est pas le cas dans la table de rapport.
class Report {
int? id;
String? activityType;
String? activityName;
String? executionMethod;
DateTime? monitoringDate;
String? monitoringPeriod;
String? activityStage;
List<ActivityItem>? activityItems;
List<RealizationItem>? realizationItems;
List<ActivityAnalysis>? activityAnalysis;
List<File>? monitoringImages;
String? conclusion;
}
Classe Helper
Ensuite, j’ai créé une autre classe d’aide qui possède pratiquement les mêmes méthodes que ReportDBHelper, mais qui prend en compte les arguments et les résultats de Report, et non Map<String, dynamic>, afin de faciliter l’appel des fonctions; je l’ai appelé ReportHelper . J’ai également créé toMap() et fromMap() pour convertir la classe Report en Map et vice-versa.
class ReportHelper {
final ReportDBHelper _dbHelper = ReportDBHelper.instance;
Future<int> insert(Report report) async {
return _dbHelper.insert(toMap(report));
}
Future<Report> queryById(int id) async {
return fromMap(await _dbHelper.queryById(id));
}
Future<List<Report>> queryAll() async {
List<Map<String, dynamic>> reportMapList = await _dbHelper.queryAll();
return reportMapList.map((e) => fromMap(e)).toList();
}
Future<int> delete(int id) async {
return _dbHelper.delete(id);
}
Future<int> update(Report report) async {
return _dbHelper.update(toMap(report));
}
Map<String, dynamic> toMap(Report report) {
return {
ReportDBHelper.columnId: report.id,
ReportDBHelper.columnProjectId: 1, // TODO get project id
ReportDBHelper.columnActivityType: report.activityType,
ReportDBHelper.columnActivityName: report.activityName,
ReportDBHelper.columnExecutionMethod: report.executionMethod,
ReportDBHelper.columnMonitoringDate:
report.monitoringDate?.millisecondsSinceEpoch,
ReportDBHelper.columnMonitoringPeriod: report.monitoringPeriod,
ReportDBHelper.columnActivityStage: report.activityStage,
ReportDBHelper.columnConclusion: report.conclusion,
};
}
Report fromMap(Map<String, dynamic> map) {
return Report()
..id = map[ReportDBHelper.columnId] as int
..activityType = map[ReportDBHelper.columnActivityType] as String
..activityName = map[ReportDBHelper.columnActivityName] as String
..executionMethod = map[ReportDBHelper.columnExecutionMethod] as String
..monitoringDate = DateTime.fromMillisecondsSinceEpoch(
map[ReportDBHelper.columnMonitoringDate] as int)
..monitoringPeriod = map[ReportDBHelper.columnMonitoringPeriod] as String
..activityStage = map[ReportDBHelper.columnActivityStage] as String
..conclusion = map[ReportDBHelper.columnConclusion] as String;
}
}
Utilisation
Maintenant, tout ce que vous avez à faire pour stocker un objet Report dans la base de données est très simple. Il vous suffit d’appeler vos méthodes à partir de ReportHelper.
final ReportHelper _reportHelper = ReportHelper();
Future inYourMethod() async {
await _reportHelper.insert(report); // insert a Report object
await _reportHelper.queryAll(); // get all Report
await _reportHelper.queryById(id); // get a report with that id number
await _reportHelper.delete(id); // delete a report with that id number
await _reportHelper.update(report); // update a Report object
}