Flutter Base de Données et persistance

Le module Flutter permettant de gérer une base de donnée est SQFLITE (SQL Flutter Lite) mais il existe ausi une solution pour conserver des données dans une base locale avec la solution MOOR renommée DRIFT

https://pub.dev/packages/moor et https://pub.dev/packages/drift

Dernier Tuto (notamment avec l’aspect BATCH) : https://educity.app/flutter/the-right-way-to-use-sqlite-in-flutter-apps-using-sqflite-package-with-examples

Pour index unique auto incrémenté :

id INTEGER PRIMARY KEY AUTOINCREMENT,

Pour éviter les doublons et les champs nuls :

name TEXT UNIQUE NOT NULL,

Pour insérer des données en batch avec des champs de type Unique :

 /// Initialize batch
    final batch = dbClient?.batch();
    JsonList.forEach((val) {
      Radios ligneradio = Radios.fromMap(val);
      batch!.insert("Radios", ligneradio.toMap(),
          conflictAlgorithm: ConflictAlgorithm.replace);
      //print(ligneradio.toMap());
    });

Pour le null safety :

Avec la list view : https://www.kindacode.com/article/flutter-sqlite/

Exemple avec listview : https://camposha.info/flutter/fr/flutter-sqflite/#gsc.tab=0

Pour initialiser une liste :

class _TopTenListState extends State<TopTenList> {
  bool _isLoading = true;

  List<RadiosFavList> _favlist = [];

  void _refreshJournals() async {
    final data = await db.fetchRadiosFav();
    setState(() {
      _favlist = data;
      _isLoading = false;
    });
  }
  @override
  void initState() {
    super.initState();
    _refreshJournals(); // Loading the diary when the app starts
  }
  
  @override
   Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[400],
      appBar: AppBar(
        automaticallyImplyLeading: false,
        title: Text(
          "Top Ten",
          style: TextStyle(color: Colors.pinkAccent[100]),
        ),
        centerTitle: true,
        actions: <Widget>[
          IconButton(
              icon: Icon(Icons.sort_by_alpha),
              tooltip: "Sort",
              onPressed: sorting),
        ],
      ),
      body: _isLoading
          ? const Center(
        child: CircularProgressIndicator(),
      )
          : ReorderableListView(
        onReorder: onReorder,
        children: _getListItems(),
      ),
    );
  }
List<Widget> _getListItems() => _favlist
      .asMap()
      .map((i, item) => MapEntry(i, _buildTenableListTile(item, i)))
      .values
      .toList();

1°) Pour SQLITE, ajout de la dépendance :

Dans le fichier pubspec.yaml, ajouter sous sdk: flutter

Ce qui donne :
dependencies:
  flutter:
    sdk: flutter
  sqflite: ^1.3.2+2
  path_provider: ^1.6.27

2°) Création du fichier gérant la base de données :

Exemple, création d’une base de données « RADIOS » qui possédera 2 tables « CHAINES » et « FAVORIS » et les fonctions nécessaires pour créer la base et les tables, pour supprimer la table « FAVORIS » pour lister le contenu des enregistrements des deux tables et pour insérer dans la table « FAVORIS » une série d’enregistrements de la table « CHAINES » tagués.

3°) Affichage de la liste de toutes les radios

Comptage du nombre de ligne de la table :

Future countChainesTable() async {
  int count;
  var dbFav = await db;
  count = Sqflite.firstIntValue(
      await dbFav.rawQuery('SELECT COUNT(*) FROM RadioChaines'));
  return (count);
}

La requête SQL récupérant toutes les radios est la suivante :

/// Retourne toutes les chaines de radios au format de liste StationRadio
Future getAllChaines() async {
var db = await SqliteDB().db;
var result = await db.rawQuery("SELECT id, title FROM RadioChaines");
return result;
}

La classe permettant d’afficher une liste comportant l’ID de la station et le nom de la radio est la suivante :

class StationRadio {
  int id;
  String title;
  StationRadio(this.id, this.title);
  @override
  String toString() {
    return '{ ${this.id}, ${this.title} }';
  }
}

La récupération de la liste avec le couple ID ; RADIO est :

//-- Création de la liste contenant toutes les radios disponibles
_counter = await db.countChainesTable();
var resultat;
if (_counter > 0) {
  resultat = await db.getAllChaines();
  print(resultat); // Pour DEBUG
  for (int i = 0; i < _counter; i++) {
    Map<String, dynamic> tteradios = resultat[i];
    print(tteradios['id']);
    print(tteradios['title']);
    ToutesRadios.add(StationRadio(tteradios['id'],tteradios['title']));
  }
}

Mise à jour de la table à partir d’une liste d’éléments :

Future UpdateFavorites(List SelectedRadio) async {
if (SelectedRadio == null || SelectedRadio.isEmpty) {
return 0;
}
var db = await SqliteDB().db;
await db
.rawQuery("UPDATE RadioChaines SET isselected ='0' WHERE isselected='1'");

for (int i = 0; i < SelectedRadio.length; i++) {
  await db.rawQuery("UPDATE RadioChaines SET isselected ='1' WHERE id='" +
      SelectedRadio[i].id.toString()+"'");
}
return SelectedRadio.length;}

Références :

https://medium.com/flutter-community/using-sqlite-in-flutter-187c1a82e8b

https://www.javacodegeeks.com/2020/06/using-sqlite-in-flutter-tutorial.html

https://stackoverflow.com/questions/55038667/error-a-value-of-type-dynamic-cant-be-assigned-to-a-variable-of-type-string

https://stackoverflow.com/questions/60221922/map-of-object-containing-a-listobject-for-sqlite

https://stackoverflow.com/questions/54102043/how-to-do-a-database-update-with-sqflite-in-flutter

3°) Ecrire le résultat d’une API dans une base de donnée :

https://medium.com/swlh/flutter-get-data-from-a-rest-api-and-save-locally-in-a-sqlite-database-9a9de5867939

4°) Afficher le résultat d’une requête SQLFLITE dans une listview :

https://blog.bajarangisoft.com/blog/how-to-populate-listview-with-database-in-flutter-app

https://stackoverflow.com/questions/53634172/flutter-fetch-records-from-database-and-display-in-listview-builder

2°) Création des répertoires et des fichiers de paramétrage :

Sous lib, créer le répertoire database, dans database créez un fichier database_helper.dart qui va contenir les méthoses utiles pour manipuler les données de la base.

import 'dart:async';
import 'dart:io' as io;

import 'package:flutter_app_db/database/record.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

class DatabaseHelper {
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
static Database _db;

Future<Database> get db async {
if (_db != null) return _db;
_db = await initDb();
return _db;
}

DatabaseHelper.internal();

initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "main.db");
var theDb = await openDatabase(path, version: 1, onCreate: _onCreate);
return theDb;
}

void _onCreate(Database db, int version) async {
// When creating the db, create the table
await db.execute(
"CREATE TABLE User(id INTEGER PRIMARY KEY, firstname TEXT, lastname TEXT, dob TEXT)");
}

Future<int> saveUser(User user) async {
var dbClient = await db;
int res = await dbClient.insert("User", user.toMap());
return res;
}

Future<List<User>> getUser() async {
var dbClient = await db;
List<Map> list = await dbClient.rawQuery('SELECT * FROM User');
List<User> employees = new List();
for (int i = 0; i < list.length; i++) {
var user =
new User(list[i]["firstname"], list[i]["lastname"], list[i]["dob"]);
user.setUserId(list[i]["id"]);
employees.add(user);
}
print(employees.length);
return employees;
}

Future<int> deleteUsers(User user) async {
var dbClient = await db;

int res =
await dbClient.rawDelete('DELETE FROM User WHERE id = ?', [user.id]);
return res;
}

Future<bool> update(User user) async {
var dbClient = await db;
int res = await dbClient.update("User", user.toMap(),
where: "id = ?", whereArgs: <int>[user.id]);
return res > 0 ? true : false;
}
}

Il est possible de déclarer plusieurs tables dans la base de données

 void _createDb(Database db, int newVersion) async {
 await db.execute('''
   create table $carTable (
    $columnCarId integer primary key autoincrement,
    $columnCarTitle text not null
   )''');
 await db.execute('''
   create table $userTable(
    $userId integer primary key autoincrement,
    $name text not null
   )''');
  }

Dans le répertoire database, créez un fichier record.dart qui contiendra la classe définissant un enregistrement dans la table

class User {

  int id;
  String _firstName;
  String _lastName;
  String _dob;

  User(this._firstName, this._lastName, this._dob);

  User.map(dynamic obj) {
    this._firstName = obj["firstname"];
    this._lastName = obj["lastname"];
    this._dob = obj["dob"];
  }

  String get firstName => _firstName;

  String get lastName => _lastName;

  String get dob => _dob;

  Map<String, dynamic> toMap() {
    var map = new Map<String, dynamic>();
    map["firstname"] = _firstName;
    map["lastname"] = _lastName;
    map["dob"] = _dob;
    return map;
  }
  void setUserId(int id) {
    this.id = id;
  }
}

3°) Appel :

main.dart

avec list view list.dart

4°) Importation / édition d’une base existante :

a) méthode avec Android Studio :

Chargez dans Android Studio l’application qui a une base de données et connectez votre téléphone avec l’application installée.

  • S’assurer qu’ADB est bien fonctionnel
    • Open File -> Project Structure;
    • In Project, select valid Project SDK;
    • In Modules -> myapp_android -> Dependencies, select a valid Module SDK
    • Then close AS, kill adb process in Task Manager, do adb start-server in command line and start AS(cf https://github.com/flutter/flutter/issues/55557
  • Ouvrez Device File Explorer 
    • Allez dans data\data\<<Nom_de_l’application>>
    • Téléchargez la base
    • Editez la via un éditeur
    • Puis Upload

b) Via programmation :

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import "package:path/path.dart";
import 'package:sqflite/sqflite.dart';
import 'dart:io' as io;

class SqliteDB {
  static const NEW_DB_VERSION = 2;
  static final SqliteDB _instance = new SqliteDB.internal();

  factory SqliteDB() => _instance;
  static Database _db;

  Future<Database> get db async {
    if (_db != null) {
      return _db;
    }
    _db = await initDb();
    return _db;
  }

  SqliteDB.internal();

  ////////////////////////////////////////////////////////////////////////////////////////////////:

  Future<Database> initDb() async {

    final databasesPath = await getDatabasesPath();
    final path = join(databasesPath, "database.db");

    var db = await openDatabase(path);

    //if database does not exist yet it will return version 0
    if (await db.getVersion() < NEW_DB_VERSION) {

      db.close();

      //delete the old database so you can copy the new one
      await deleteDatabase(path);

      try {
        await Directory(dirname(path)).create(recursive: true);
      } catch (_) {}

      //copy db from assets to database folder
      ByteData data = await rootBundle.load("assets/db/radios.db");
      List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
      await io.File(path).writeAsBytes(bytes, flush: true);

      //open the newly created db
      db = await openDatabase(path);

      //set the new version to the copied db so you do not need to do it manually on your bundled database.db
      db.setVersion(NEW_DB_VERSION);

    }
    return db;
  }

https://stackoverflow.com/questions/60458746/is-there-a-way-to-check-the-database-version-in-a-flutter-app-using-sqflite

https://github.com/tekartik/sqflite/blob/master/sqflite/doc/opening_asset_db.md

Références :

https://www.developerlibs.com/2018/07/flutter-sqlite-database-example.html

https://www.tutorialspoint.com/flutter/flutter_database_concepts.htm

https://medium.com/flutter-community/using-sqlite-in-flutter-187c1a82e8b

https://www.javacodegeeks.com/2020/06/using-sqlite-in-flutter-tutorial.html

Articles récents
Commentaires récents
fatima dans Bienvenue !
AdminDroid dans Bienvenue !
fatima dans Bienvenue !
Archives
Catégories
%d blogueurs aiment cette page :