Flutter - Concepts de base de données

Flutter fournit de nombreux packages avancés pour travailler avec des bases de données. Les packages les plus importants sont -

  • sqflite - Utilisé pour accéder et manipuler la base de données SQLite, et

  • firebase_database - Utilisé pour accéder et manipuler la base de données NoSQL hébergée dans le cloud de Google.

Dans ce chapitre, discutons chacun d'eux en détail.

SQLite

La base de données SQLite est le moteur de base de données intégré SQL de facto et standard. C'est un petit moteur de base de données qui a fait ses preuves. Le package sqflite fournit de nombreuses fonctionnalités pour travailler efficacement avec la base de données SQLite. Il fournit des méthodes standard pour manipuler le moteur de base de données SQLite. La fonctionnalité principale fournie par le package sqflite est la suivante -

  • Créer / ouvrir (méthode openDatabase) une base de données SQLite.

  • Exécuter l'instruction SQL (méthode d'exécution) sur la base de données SQLite.

  • Méthodes de requête avancées (méthode de requête) à réduire au code requis pour interroger et obtenir des informations de la base de données SQLite.

Créons une application produit pour stocker et extraire des informations produit à partir d'un moteur de base de données SQLite standard à l'aide du package sqflite et comprendre le concept derrière la base de données SQLite et le package sqflite.

  • Créez une nouvelle application Flutter dans le studio Android, product_sqlite_app.

  • Remplacez le code de démarrage par défaut (main.dart) par notre code product_rest_app .

  • Copiez le dossier des actifs de product_nav_app vers product_rest_app et ajoutez des actifs dans le fichier * pubspec.yaml`.

flutter: 
   assets: 
      - assets/appimages/floppy.png 
      - assets/appimages/iphone.png 
      - assets/appimages/laptop.png 
      - assets/appimages/pendrive.png 
      - assets/appimages/pixel.png 
      - assets/appimages/tablet.png
  • Configurez le package sqflite dans le fichier pubspec.yaml comme indiqué ci-dessous -

dependencies: sqflite: any

Utilisez le dernier numéro de version de sqflite à la place de tout

  • Configurez le package path_provider dans le fichier pubspec.yaml comme indiqué ci-dessous -

dependencies: path_provider: any
  • Ici, le package path_provider est utilisé pour obtenir le chemin du dossier temporaire du système et le chemin de l'application. Utilisez le dernier numéro de version de sqflite à la place de tout .

  • Le studio Android avertira que le pubspec.yaml est mis à jour.

  • Cliquez sur l'option Obtenir les dépendances. Le studio Android obtiendra le package sur Internet et le configurera correctement pour l'application.

  • Dans la base de données, nous avons besoin de la clé primaire, de l'identifiant comme champ supplémentaire ainsi que des propriétés du produit telles que le nom, le prix, etc., donc, ajoutez la propriété id dans la classe Product. Ajoutez également une nouvelle méthode, toMap pour convertir l'objet produit en objet Map. fromMap et toMap sont utilisés pour sérialiser et désérialiser l'objet Product et il est utilisé dans les méthodes de manipulation de base de données.

class Product { 
   final int id; 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   static final columns = ["id", "name", "description", "price", "image"]; 
   Product(this.id, this.name, this.description, this.price, this.image); 
   factory Product.fromMap(Map<String, dynamic> data) {
      return Product( 
         data['id'], 
         data['name'], 
         data['description'], 
         data['price'], 
         data['image'], 
      ); 
   } 
   Map<String, dynamic> toMap() => {
      "id": id, 
      "name": name, 
      "description": description, 
      "price": price, 
      "image": image 
   }; 
}
  • Créez un nouveau fichier, Database.dart dans le dossier lib pour écrire les fonctionnalités liées à SQLite .

  • Importez l'instruction d'importation nécessaire dans Database.dart.

import 'dart:async'; 
import 'dart:io'; 
import 'package:path/path.dart'; 
import 'package:path_provider/path_provider.dart'; 
import 'package:sqflite/sqflite.dart'; 
import 'Product.dart';
  • Notez les points suivants ici -

    • async est utilisé pour écrire des méthodes asynchrones.

    • io est utilisé pour accéder aux fichiers et répertoires.

    • path est utilisé pour accéder à la fonction utilitaire de base de dart liée aux chemins de fichiers.

    • path_provider est utilisé pour obtenir le chemin d'accès temporaire et d'application.

    • sqflite est utilisé pour manipuler la base de données SQLite.

  • Créer une nouvelle classe SQLiteDbProvider

  • Déclarez un objet SQLiteDbProvider statique basé sur un singleton comme spécifié ci-dessous -

class SQLiteDbProvider { 
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
}
  • L'objet SQLiteDBProvoider et sa méthode sont accessibles via la variable statique db.

SQLiteDBProvoider.db.<emthod>
  • Créez une méthode pour obtenir la base de données (option Future) de type Future <Database>. Créez une table de produits et chargez les données initiales lors de la création de la base de données elle-même.

Future<Database> get database async { 
   if (_database != null) 
   return _database; 
   _database = await initDB(); 
   return _database; 
}
initDB() async { 
   Directory documentsDirectory = await getApplicationDocumentsDirectory(); 
   String path = join(documentsDirectory.path, "ProductDB.db"); 
   return await openDatabase(
      path, 
      version: 1,
      onOpen: (db) {}, 
      onCreate: (Database db, int version) async {
         await db.execute(
            "CREATE TABLE Product ("
            "id INTEGER PRIMARY KEY,"
            "name TEXT,"
            "description TEXT,"
            "price INTEGER," 
            "image TEXT" ")"
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [1, "iPhone", "iPhone is the stylist phone ever", 1000, "iphone.png"]
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [2, "Pixel", "Pixel is the most feature phone ever", 800, "pixel.png"]
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [3, "Laptop", "Laptop is most productive development tool", 2000, "laptop.png"]\
         ); 
         await db.execute( 
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [4, "Tablet", "Laptop is most productive development tool", 1500, "tablet.png"]
         );
         await db.execute( 
            "INSERT INTO Product 
            ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [5, "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png"]
         );
         await db.execute( 
            "INSERT INTO Product 
            ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [6, "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png"]
         ); 
      }
   ); 
}
  • Ici, nous avons utilisé les méthodes suivantes -

    • getApplicationDocumentsDirectory - Renvoie le chemin du répertoire de l'application

    • join- Utilisé pour créer un chemin d'accès spécifique au système. Nous l'avons utilisé pour créer le chemin de la base de données.

    • openDatabase - Utilisé pour ouvrir une base de données SQLite

    • onOpen - Utilisé pour écrire du code lors de l'ouverture d'une base de données

    • onCreate - Utilisé pour écrire du code lors de la création d'une base de données pour la première fois

    • db.execute- Utilisé pour exécuter des requêtes SQL. Il accepte une requête. Si la requête a un espace réservé (?), Elle accepte les valeurs sous forme de liste dans le deuxième argument.

  • Écrivez une méthode pour obtenir tous les produits dans la base de données -

Future<List<Product>> getAllProducts() async { 
   final db = await database; 
   List<Map> 
   results = await db.query("Product", columns: Product.columns, orderBy: "id ASC"); 
   
   List<Product> products = new List(); 
   results.forEach((result) { 
      Product product = Product.fromMap(result); 
      products.add(product); 
   }); 
   return products; 
}
  • Ici, nous avons fait ce qui suit -

    • Méthode de requête utilisée pour récupérer toutes les informations sur le produit. query fournit un raccourci pour interroger les informations d'une table sans écrire la requête entière. La méthode de requête générera elle-même la requête appropriée en utilisant notre entrée comme les colonnes, orderBy, etc.,

    • Utilisation de la méthode fromMap de Product pour obtenir les détails du produit en bouclant l'objet de résultats, qui contient toutes les lignes de la table.

  • Écrivez une méthode pour obtenir un produit spécifique à id

Future<Product> getProductById(int id) async {
   final db = await database; 
   var result = await db.query("Product", where: "id = ", whereArgs: [id]); 
   return result.isNotEmpty ? Product.fromMap(result.first) : Null; 
}
  • Ici, nous avons utilisé where et whereArgs pour appliquer des filtres.

  • Créez trois méthodes - méthode d'insertion, de mise à jour et de suppression pour insérer, mettre à jour et supprimer le produit de la base de données.

insert(Product product) async { 
   final db = await database; 
   var maxIdResult = await db.rawQuery(
      "SELECT MAX(id)+1 as last_inserted_id FROM Product");

   var id = maxIdResult.first["last_inserted_id"]; 
   var result = await db.rawInsert(
      "INSERT Into Product (id, name, description, price, image)" 
      " VALUES (?, ?, ?, ?, ?)", 
      [id, product.name, product.description, product.price, product.image] 
   ); 
   return result; 
}
update(Product product) async { 
   final db = await database; 
   var result = await db.update("Product", product.toMap(), 
   where: "id = ?", whereArgs: [product.id]); return result; 
} 
delete(int id) async { 
   final db = await database; 
   db.delete("Product", where: "id = ?", whereArgs: [id]); 
}
  • Le code final de Database.dart est le suivant -

import 'dart:async'; 
import 'dart:io'; 
import 'package:path/path.dart'; 
import 'package:path_provider/path_provider.dart'; 
import 'package:sqflite/sqflite.dart'; 
import 'Product.dart'; 

class SQLiteDbProvider {
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
   
   Future<Database> get database async {
      if (_database != null) 
      return _database; 
      _database = await initDB(); 
      return _database; 
   } 
   initDB() async {
      Directory documentsDirectory = await 
      getApplicationDocumentsDirectory(); 
      String path = join(documentsDirectory.path, "ProductDB.db"); 
      return await openDatabase(
         path, version: 1, 
         onOpen: (db) {}, 
         onCreate: (Database db, int version) async {
            await db.execute(
               "CREATE TABLE Product (" 
               "id INTEGER PRIMARY KEY," 
               "name TEXT," 
               "description TEXT," 
               "price INTEGER," 
               "image TEXT"")"
            ); 
            await db.execute(
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [1, "iPhone", "iPhone is the stylist phone ever", 1000, "iphone.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [2, "Pixel", "Pixel is the most feature phone ever", 800, "pixel.png"]
            );
            await db.execute(
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [3, "Laptop", "Laptop is most productive development tool", 2000, "laptop.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [4, "Tablet", "Laptop is most productive development tool", 1500, "tablet.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [5, "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png"]
            );
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [6, "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png"]
            ); 
         }
      ); 
   }
   Future<List<Product>> getAllProducts() async {
      final db = await database; 
      List<Map> results = await db.query(
         "Product", columns: Product.columns, orderBy: "id ASC"
      ); 
      List<Product> products = new List();   
      results.forEach((result) {
         Product product = Product.fromMap(result); 
         products.add(product); 
      }); 
      return products; 
   } 
   Future<Product> getProductById(int id) async {
      final db = await database; 
      var result = await db.query("Product", where: "id = ", whereArgs: [id]); 
      return result.isNotEmpty ? Product.fromMap(result.first) : Null; 
   } 
   insert(Product product) async { 
      final db = await database; 
      var maxIdResult = await db.rawQuery("SELECT MAX(id)+1 as last_inserted_id FROM Product"); 
      var id = maxIdResult.first["last_inserted_id"]; 
      var result = await db.rawInsert(
         "INSERT Into Product (id, name, description, price, image)" 
         " VALUES (?, ?, ?, ?, ?)", 
         [id, product.name, product.description, product.price, product.image] 
      ); 
      return result; 
   } 
   update(Product product) async { 
      final db = await database; 
      var result = await db.update(
         "Product", product.toMap(), where: "id = ?", whereArgs: [product.id]
      ); 
      return result; 
   } 
   delete(int id) async { 
      final db = await database; 
      db.delete("Product", where: "id = ?", whereArgs: [id]);
   } 
}
  • Modifiez la méthode principale pour obtenir les informations sur le produit.

void main() {
   runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); 
}
  • Ici, nous avons utilisé la méthode getAllProducts pour récupérer tous les produits de la base de données.

  • Exécutez l'application et voyez les résultats. Il sera similaire à l'exemple précédent, Accessing Product service API , sauf que les informations sur le produit sont stockées et extraites de la base de données SQLite locale.

Cloud Firestore

Firebase est une plate-forme de développement d'applications BaaS. Il fournit de nombreuses fonctionnalités pour accélérer le développement d'applications mobiles telles que le service d'authentification, le stockage dans le cloud, etc. L'une des principales fonctionnalités de Firebase est Cloud Firestore, une base de données NoSQL en temps réel basée sur le cloud.

Flutter fournit un package spécial, cloud_firestore pour programmer avec Cloud Firestore. Créons une boutique de produits en ligne dans le Cloud Firestore et créons une application pour accéder à la boutique de produits.

  • Créez une nouvelle application Flutter dans le studio Android, product_firebase_app.

  • Remplacez le code de démarrage par défaut (main.dart) par notre code product_rest_app .

  • Copiez le fichier Product.dart de product_rest_app dans le dossier lib.

class Product { 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Product(this.name, this.description, this.price, this.image); 
   factory Product.fromMap(Map<String, dynamic> json) {
      return Product( 
         json['name'], 
         json['description'], 
         json['price'], 
         json['image'], 
      ); 
   }
}
  • Copiez le dossier des actifs de product_rest_app vers product_firebase_app et ajoutez des actifs dans le fichier pubspec.yaml.

flutter:
   assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png
  • Configurez le package cloud_firestore dans le fichier pubspec.yaml comme indiqué ci-dessous -

dependencies: cloud_firestore: ^0.9.13+1
  • Ici, utilisez la dernière version du package cloud_firestore.

  • Le studio Android avertira que pubspec.yaml est mis à jour comme indiqué ici -

  • Cliquez sur l'option Obtenir les dépendances. Le studio Android obtiendra le package sur Internet et le configurera correctement pour l'application.

  • Créez un projet dans Firebase en suivant les étapes suivantes -

    • Créez un compte Firebase en sélectionnant Forfait gratuit sur https://firebase.google.com/pricing/.

    • Une fois le compte Firebase créé, il sera redirigé vers la page de présentation du projet. Il répertorie tous les projets basés sur Firebase et fournit une option pour créer un nouveau projet.

    • Cliquez sur Ajouter un projet et cela ouvrira une page de création de projet.

    • Entrez la base de données de l'application des produits comme nom de projet et cliquez sur l'option Créer un projet.

    • Accédez à * la console Firebase.

    • Cliquez sur Présentation du projet. Il ouvre la page de présentation du projet.

    • Cliquez sur l'icône Android. Il ouvrira les paramètres de projet spécifiques au développement Android.

    • Entrez le nom du package Android, com.tutorialspoint.flutterapp.product_firebase_app.

    • Cliquez sur Enregistrer l'application. Il génère un fichier de configuration de projet, google_service.json.

    • Téléchargez google_service.json, puis déplacez-le dans le répertoire android / app du projet. Ce fichier est la connexion entre notre application et Firebase.

    • Ouvrez android / app / build.gradle et incluez le code suivant -

apply plugin: 'com.google.gms.google-services'
    • Ouvrez android / build.gradle et incluez la configuration suivante -

buildscript {
   repositories { 
      // ... 
   } 
   dependencies { 
      // ... 
      classpath 'com.google.gms:google-services:3.2.1' // new 
   } 
}

    Ici, le plugin et le chemin de classe sont utilisés dans le but de lire le fichier google_service.json.

    • Ouvrez android / app / build.gradle et incluez également le code suivant.

android {
   defaultConfig { 
      ... 
      multiDexEnabled true 
   } 
   ...
}
dependencies {
   ... 
   compile 'com.android.support: multidex:1.0.3' 
}

    Cette dépendance permet à l'application Android d'utiliser plusieurs fonctionnalités dex.

    • Suivez les étapes restantes dans la console Firebase ou ignorez-les simplement.

  • Créez un magasin de produits dans le projet nouvellement créé en suivant les étapes suivantes:

    • Accédez à la console Firebase.

    • Ouvrez le projet nouvellement créé.

    • Cliquez sur l'option Base de données dans le menu de gauche.

    • Cliquez sur l'option Créer une base de données.

    • Cliquez sur Démarrer en mode test, puis sur Activer.

    • Cliquez sur Ajouter une collection. Entrez le produit comme nom de collection, puis cliquez sur Suivant.

    • Entrez les informations sur le produit comme indiqué dans l'image ici -

  • Ajoutez des informations supplémentaires sur le produit à l'aide des options Ajouter un document .

  • Ouvrez le fichier main.dart, importez le fichier de plug-in Cloud Firestore et supprimez le package http.

import 'package:cloud_firestore/cloud_firestore.dart';
  • Supprimez parseProducts et mettez à jour fetchProducts pour récupérer les produits de Cloud Firestore au lieu de l'API Product service.

Stream<QuerySnapshot> fetchProducts() { 
   return Firestore.instance.collection('product').snapshots(); }
  • Ici, la méthode Firestore.instance.collection est utilisée pour accéder à la collection de produits disponible dans le magasin cloud. Firestore.instance.collection fournit de nombreuses options pour filtrer la collection afin d'obtenir les documents nécessaires. Mais nous n'avons appliqué aucun filtre pour obtenir toutes les informations sur les produits.

  • Cloud Firestore fournit la collection via le concept Dart Stream et modifie ainsi le type de produits dans les widgets MyApp et MyHomePage de Future <list <Product>> en Stream <QuerySnapshot>.

  • Modifiez la méthode de génération du widget MyHomePage pour utiliser StreamBuilder au lieu de FutureBuilder.

@override 
Widget build(BuildContext context) {
   return Scaffold(
      appBar: AppBar(title: Text("Product Navigation")), 
      body: Center(
         child: StreamBuilder<QuerySnapshot>(
            stream: products, builder: (context, snapshot) {
               if (snapshot.hasError) print(snapshot.error); 
               if(snapshot.hasData) {
                  List<DocumentSnapshot> 
                  documents = snapshot.data.documents; 
                  
                  List<Product> 
                  items = List<Product>(); 
                  
                  for(var i = 0; i < documents.length; i++) { 
                     DocumentSnapshot document = documents[i]; 
                     items.add(Product.fromMap(document.data)); 
                  } 
                  return ProductBoxList(items: items);
               } else { 
                  return Center(child: CircularProgressIndicator()); 
               }
            }, 
         ), 
      )
   ); 
}
  • Ici, nous avons récupéré les informations sur le produit en tant que type List <DocumentSnapshot>. Puisque notre widget, ProductBoxList n'est pas compatible avec les documents, nous avons converti les documents en type List <Product> et l'avons ensuite utilisé.

  • Enfin, exécutez l'application et voyez le résultat. Depuis, nous avons utilisé les mêmes informations produit que celles de l' application SQLite et changé le support de stockage uniquement, l'application résultante est identique à l' application d' application SQLite .