SQLite Flutter avec sqflite
SQLite avec sqflite
Section titled “SQLite avec sqflite”Pour des données plus complexes et des relations entre entités, SQLite est la solution recommandée.
Installation
Section titled “Installation”dependencies: sqflite: ^2.3.0 path: ^1.8.3Configuration de Base
Section titled “Configuration de Base”import 'package:sqflite/sqflite.dart';import 'package:path/path.dart';
class DatabaseHelper { static final DatabaseHelper _instance = DatabaseHelper._internal(); factory DatabaseHelper() => _instance; DatabaseHelper._internal();
Database? _database;
Future<Database> get database async { _database ??= await _initDatabase(); return _database!; }
Future<Database> _initDatabase() async { String path = join(await getDatabasesPath(), 'app_database.db');
return await openDatabase( path, version: 1, onCreate: _onCreate, ); }
Future<void> _onCreate(Database db, int version) async { await db.execute(''' CREATE TABLE users( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, created_at TEXT NOT NULL ) ''');
await db.execute(''' CREATE TABLE tasks( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, description TEXT, completed INTEGER NOT NULL DEFAULT 0, user_id INTEGER, FOREIGN KEY (user_id) REFERENCES users (id) ) '''); }}Modèle de Données et Repository
Section titled “Modèle de Données et Repository”class User { final int? id; final String name; final String email; final DateTime createdAt;
User({this.id, required this.name, required this.email, required this.createdAt});
Map<String, dynamic> toMap() { return { 'id': id, 'name': name, 'email': email, 'created_at': createdAt.toIso8601String(), }; }
factory User.fromMap(Map<String, dynamic> map) { return User( id: map['id'], name: map['name'], email: map['email'], createdAt: DateTime.parse(map['created_at']), ); }}
class UserRepository { final DatabaseHelper _dbHelper = DatabaseHelper();
Future<int> insertUser(User user) async { final db = await _dbHelper.database; return await db.insert('users', user.toMap()); }
Future<List<User>> getAllUsers() async { final db = await _dbHelper.database; final List<Map<String, dynamic>> maps = await db.query('users');
return List.generate(maps.length, (i) => User.fromMap(maps[i])); }
Future<User?> getUserById(int id) async { final db = await _dbHelper.database; final List<Map<String, dynamic>> maps = await db.query( 'users', where: 'id = ?', whereArgs: [id], );
if (maps.isNotEmpty) { return User.fromMap(maps.first); } return null; }
Future<int> updateUser(User user) async { final db = await _dbHelper.database; return await db.update( 'users', user.toMap(), where: 'id = ?', whereArgs: [user.id], ); }
Future<int> deleteUser(int id) async { final db = await _dbHelper.database; return await db.delete( 'users', where: 'id = ?', whereArgs: [id], ); }}Transactions
Section titled “Transactions”Future<void> transferTasks(int fromUserId, int toUserId) async { final db = await DatabaseHelper().database;
await db.transaction((txn) async { // Vérifier que les utilisateurs existent final fromUser = await txn.query('users', where: 'id = ?', whereArgs: [fromUserId]); final toUser = await txn.query('users', where: 'id = ?', whereArgs: [toUserId]);
if (fromUser.isEmpty || toUser.isEmpty) { throw Exception('Utilisateur non trouvé'); }
// Transférer les tâches await txn.update( 'tasks', {'user_id': toUserId}, where: 'user_id = ?', whereArgs: [fromUserId], ); });}Gestion des Migrations
Section titled “Gestion des Migrations”class DatabaseHelper { Future<Database> _initDatabase() async { return await openDatabase( path, version: 2, // Version mise à jour onCreate: _onCreate, onUpgrade: _onUpgrade, ); }
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async { if (oldVersion < 2) { await db.execute('ALTER TABLE users ADD COLUMN avatar_url TEXT'); } }}Bonnes Pratiques
Section titled “Bonnes Pratiques”- Singleton Pattern : Utiliser une seule instance de DatabaseHelper
- Transactions : Grouper les opérations liées pour maintenir la cohérence
- Index : Créer des index sur les colonnes fréquemment interrogées
- Migrations : Planifier et tester les changements de schéma
- Gestion d’erreurs : Toujours gérer les exceptions SQLite
- Fermeture : Fermer la base de données quand nécessaire
- Tests : Écrire des tests unitaires pour toutes les opérations
Cas d’Usage Recommandés
Section titled “Cas d’Usage Recommandés”- Applications CRUD : Gestion d’utilisateurs, tâches, notes
- Données relationnelles : Relations entre entités
- Requêtes complexes : Jointures, agrégations, filtres avancés
- Intégrité référentielle : Contraintes de clés étrangères
- Cache local : Stockage de données synchronisées avec serveur