Le langage 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
- sqflite: avec le n° de version pour inclure SQFlite
- path_provider: ^1.6.27 pour accéder au chemin de stockage de la base de données
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/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 :
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
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; }
5°) Chargement d’une base de données depuis le répertoire « Assets » :
a) Créer la base de données avec les tables désirées
b) Ajouter dans pubspec la ligne Assets (et créer les répertoires à la racine du projet) :
assets: - assets/DB
et les dépendances :
sqflite: ^2.0.2
path_provider: ^2.0.9
c) Copier la fonction :
d) Copier le fichier de base de données .db dans assets/DB
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