Flutter et SQLite

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
}

0 Shares:
Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

You May Also Like
Lire plus

Flutter 3.13 : Les dernières fonctionnalités

La dernière mise à jour du framework d'interface utilisateur multiplateforme Flutter de Google fait d'Impeller le moteur de rendu graphique par défaut sur iOS. Le défilement en deux dimensions est aussi de la partie.