Flattern - Datenbankkonzepte

Flutter bietet viele erweiterte Pakete für die Arbeit mit Datenbanken. Die wichtigsten Pakete sind -

  • sqflite - Wird verwendet, um auf die SQLite-Datenbank zuzugreifen und diese zu bearbeiten

  • firebase_database - Wird verwendet, um über Google auf eine in der Cloud gehostete NoSQL-Datenbank zuzugreifen und diese zu bearbeiten.

Lassen Sie uns in diesem Kapitel jeden einzelnen von ihnen ausführlich besprechen.

SQLite

Die SQLite-Datenbank ist das De-facto- und Standard-SQL-basierte Embedded-Datenbankmodul. Es ist eine kleine und bewährte Datenbank-Engine. Das sqflite-Paket bietet viele Funktionen, um effizient mit der SQLite-Datenbank zu arbeiten. Es bietet Standardmethoden zum Bearbeiten des SQLite-Datenbankmoduls. Die Kernfunktionalität des sqflite-Pakets lautet wie folgt:

  • Erstellen / Öffnen (openDatabase-Methode) einer SQLite-Datenbank.

  • Führen Sie die SQL-Anweisung (Methode execute) für die SQLite-Datenbank aus.

  • Erweiterte Abfragemethoden (Abfragemethode) zum Reduzieren auf Code, der zum Abfragen und Abrufen von Informationen aus der SQLite-Datenbank erforderlich ist.

Lassen Sie uns eine Produktanwendung erstellen, um Produktinformationen mithilfe des sqflite-Pakets aus einem Standard-SQLite-Datenbankmodul zu speichern und abzurufen, und das Konzept hinter der SQLite-Datenbank und dem sqflite-Paket verstehen.

  • Erstellen Sie eine neue Flutter-Anwendung in Android Studio, product_sqlite_app.

  • Ersetzen Sie den Standardstartcode (main.dart) durch unseren product_rest_app- Code.

  • Kopieren Sie den Assets-Ordner von product_nav_app nach product_rest_app und fügen Sie Assets in die Datei * pubspec.yaml` ein.

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
  • Konfigurieren Sie das sqflite-Paket in der Datei pubspec.yaml wie unten gezeigt -

dependencies: sqflite: any

Verwenden Sie die neueste Versionsnummer von sqflite anstelle von any

  • Konfigurieren Sie das Paket path_provider in der Datei pubspec.yaml wie unten gezeigt -

dependencies: path_provider: any
  • Hier wird das Paket path_provider verwendet, um den Pfad des temporären Ordners des Systems und den Pfad der Anwendung abzurufen. Verwenden Sie die neueste Versionsnummer von sqflite anstelle von any .

  • Android Studio benachrichtigt Sie, dass die Datei pubspec.yaml aktualisiert wird.

  • Klicken Sie auf die Option Abhängigkeiten abrufen. Android Studio holt das Paket aus dem Internet und konfiguriert es ordnungsgemäß für die Anwendung.

  • In der Datenbank benötigen wir Primärschlüssel, ID als zusätzliches Feld sowie Produkteigenschaften wie Name, Preis usw. Fügen Sie also die Eigenschaft id zur Produktklasse hinzu. Fügen Sie außerdem eine neue Methode hinzu, toMap, um das Produktobjekt in ein Map-Objekt zu konvertieren. fromMap und toMap werden zum Serialisieren und De-Serialisieren des Product-Objekts verwendet und bei Datenbankmanipulationsmethoden verwendet.

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 
   }; 
}
  • Erstellen Sie eine neue Datei, Database.dart, im Ordner lib, um SQLite- bezogene Funktionen zu schreiben .

  • Importieren Sie die erforderliche Importanweisung in 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';
  • Beachten Sie hier die folgenden Punkte -

    • async wird verwendet, um asynchrone Methoden zu schreiben.

    • io wird verwendet, um auf Dateien und Verzeichnisse zuzugreifen.

    • path wird verwendet, um auf die Dart Core-Dienstprogrammfunktion zuzugreifen, die sich auf Dateipfade bezieht.

    • path_provider wird verwendet, um den temporären Pfad und den Anwendungspfad abzurufen.

    • sqflite wird zum Bearbeiten der SQLite-Datenbank verwendet.

  • Erstellen Sie eine neue Klasse SQLiteDbProvider

  • Deklarieren Sie ein Singleton-basiertes statisches SQLiteDbProvider-Objekt wie unten angegeben -

class SQLiteDbProvider { 
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
}
  • Auf das SQLiteDBProvoider-Objekt und seine Methode kann über die statische DB-Variable zugegriffen werden.

SQLiteDBProvoider.db.<emthod>
  • Erstellen Sie eine Methode, um eine Datenbank (Option Future) vom Typ Future <Datenbank> abzurufen. Erstellen Sie eine Produkttabelle und laden Sie die Anfangsdaten während der Erstellung der Datenbank.

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"]
         ); 
      }
   ); 
}
  • Hier haben wir die folgenden Methoden verwendet -

    • getApplicationDocumentsDirectory - Gibt den Anwendungsverzeichnispfad zurück

    • join- Wird verwendet, um einen systemspezifischen Pfad zu erstellen. Wir haben es verwendet, um einen Datenbankpfad zu erstellen.

    • openDatabase - Wird zum Öffnen einer SQLite-Datenbank verwendet

    • onOpen - Wird zum Schreiben von Code beim Öffnen einer Datenbank verwendet

    • onCreate - Wird verwendet, um Code zu schreiben, während zum ersten Mal eine Datenbank erstellt wird

    • db.execute- Wird zum Ausführen von SQL-Abfragen verwendet. Es akzeptiert eine Abfrage. Wenn die Abfrage einen Platzhalter (?) Hat, akzeptiert sie Werte als Liste im zweiten Argument.

  • Schreiben Sie eine Methode, um alle Produkte in die Datenbank zu bekommen -

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; 
}
  • Hier haben wir folgendes getan:

    • Verwendete Abfragemethode zum Abrufen aller Produktinformationen. Die Abfrage bietet eine Verknüpfung zum Abfragen von Tabelleninformationen, ohne die gesamte Abfrage zu schreiben. Die Abfragemethode generiert die richtige Abfrage selbst mithilfe unserer Eingaben wie Spalten, orderBy usw.

    • Verwendete die fromMap-Methode des Produkts, um Produktdetails durch Schleifen des Ergebnisobjekts abzurufen, das alle Zeilen in der Tabelle enthält.

  • Schreiben Sie eine Methode, um das Produkt spezifisch zu erhalten 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; 
}
  • Hier haben wir where und whereArgs verwendet, um Filter anzuwenden.

  • Erstellen Sie drei Methoden: Einfügen, Aktualisieren und Löschen, um Produkte in die Datenbank einzufügen, zu aktualisieren und zu löschen.

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]); 
}
  • Der endgültige Code des Database.dart lautet wie folgt:

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]);
   } 
}
  • Ändern Sie die Hauptmethode, um die Produktinformationen abzurufen.

void main() {
   runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); 
}
  • Hier haben wir die Methode getAllProducts verwendet, um alle Produkte aus der Datenbank abzurufen.

  • Führen Sie die Anwendung aus und sehen Sie sich die Ergebnisse an. Es ähnelt dem vorherigen Beispiel " Zugriff auf die Produktservice-API" , außer dass die Produktinformationen gespeichert und aus der lokalen SQLite-Datenbank abgerufen werden.

Cloud Firestore

Firebase ist eine BaaS-App-Entwicklungsplattform. Es bietet viele Funktionen zur Beschleunigung der Entwicklung mobiler Anwendungen wie Authentifizierungsdienst, Cloud-Speicher usw. Eine der Hauptfunktionen von Firebase ist Cloud Firestore, eine Cloud-basierte Echtzeit-NoSQL-Datenbank.

Flutter bietet ein spezielles Paket, cloud_firestore, zum Programmieren mit Cloud Firestore. Lassen Sie uns einen Online-Produktspeicher im Cloud Firestore erstellen und eine Anwendung für den Zugriff auf den Produktspeicher erstellen.

  • Erstellen Sie eine neue Flutter-Anwendung in Android Studio, product_firebase_app.

  • Ersetzen Sie den Standardstartcode (main.dart) durch unseren product_rest_app- Code.

  • Kopieren Sie die Datei Product.dart aus product_rest_app in den Ordner 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'], 
      ); 
   }
}
  • Kopieren Sie den Assets-Ordner von product_rest_app nach product_firebase_app und fügen Sie Assets in die Datei pubspec.yaml ein.

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
  • Konfigurieren Sie das Paket cloud_firestore in der Datei pubspec.yaml wie unten gezeigt -

dependencies: cloud_firestore: ^0.9.13+1
  • Verwenden Sie hier die neueste Version des Pakets cloud_firestore.

  • Android Studio benachrichtigt Sie, dass die Datei pubspec.yaml wie hier gezeigt aktualisiert wird -

  • Klicken Sie auf die Option Abhängigkeiten abrufen. Android Studio holt das Paket aus dem Internet und konfiguriert es ordnungsgemäß für die Anwendung.

  • Erstellen Sie ein Projekt in der Firebase mit den folgenden Schritten:

    • Erstellen Sie ein Firebase-Konto, indem Sie unter Kostenloser Plan auswählen https://firebase.google.com/pricing/.

    • Sobald ein Firebase-Konto erstellt wurde, wird es zur Projektübersichtsseite weitergeleitet. Es listet alle Firebase-basierten Projekte auf und bietet eine Option zum Erstellen eines neuen Projekts.

    • Klicken Sie auf Projekt hinzufügen, um eine Projekterstellungsseite zu öffnen.

    • Geben Sie als Projektnamen die Produkt-App db ein und klicken Sie auf die Option Projekt erstellen.

    • Gehen Sie zur * Firebase-Konsole.

    • Klicken Sie auf Projektübersicht. Es öffnet sich die Projektübersichtsseite.

    • Klicken Sie auf das Android-Symbol. Es werden Projekteinstellungen geöffnet, die für die Android-Entwicklung spezifisch sind.

    • Geben Sie den Namen des Android-Pakets ein, com.tutorialspoint.flutterapp.product_firebase_app.

    • Klicken Sie auf App registrieren. Es generiert eine Projektkonfigurationsdatei, google_service.json.

    • Laden Sie google_service.json herunter und verschieben Sie es in das Android / App-Verzeichnis des Projekts. Diese Datei ist die Verbindung zwischen unserer Anwendung und Firebase.

    • Öffnen Sie android / app / build.gradle und geben Sie den folgenden Code ein:

apply plugin: 'com.google.gms.google-services'
    • Öffnen Sie android / build.gradle und geben Sie die folgende Konfiguration an:

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

    Hier werden das Plugin und der Klassenpfad zum Lesen der Datei google_service.json verwendet.

    • Öffnen Sie android / app / build.gradle und geben Sie den folgenden Code ein.

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

    Diese Abhängigkeit ermöglicht es der Android-Anwendung, mehrere Dex-Funktionen zu verwenden.

    • Befolgen Sie die verbleibenden Schritte in der Firebase-Konsole oder überspringen Sie sie einfach.

  • Erstellen Sie einen Produktspeicher im neu erstellten Projekt mit den folgenden Schritten:

    • Gehen Sie zur Firebase-Konsole.

    • Öffnen Sie das neu erstellte Projekt.

    • Klicken Sie im linken Menü auf die Option Datenbank.

    • Klicken Sie auf die Option Datenbank erstellen.

    • Klicken Sie im Testmodus auf Start und dann auf Aktivieren.

    • Klicken Sie auf Sammlung hinzufügen. Geben Sie das Produkt als Sammlungsnamen ein und klicken Sie dann auf Weiter.

    • Geben Sie die Musterproduktinformationen wie im Bild gezeigt hier ein -

  • Fügen Sie mithilfe der Optionen zum Hinzufügen von Dokumenten zusätzliche Produktinformationen hinzu .

  • Öffnen Sie die Datei main.dart, importieren Sie die Cloud Firestore-Plugin-Datei und entfernen Sie das http-Paket.

import 'package:cloud_firestore/cloud_firestore.dart';
  • Entfernen Sie parseProducts und aktualisieren Sie fetchProducts, um Produkte aus dem Cloud Firestore anstelle der Product Service API abzurufen.

Stream<QuerySnapshot> fetchProducts() { 
   return Firestore.instance.collection('product').snapshots(); }
  • Hier wird die Firestore.instance.collection-Methode verwendet, um auf die im Cloud Store verfügbare Produktsammlung zuzugreifen. Firestore.instance.collection bietet viele Optionen zum Filtern der Sammlung, um die erforderlichen Dokumente abzurufen. Wir haben jedoch keinen Filter angewendet, um alle Produktinformationen zu erhalten.

  • Cloud Firestore stellt die Sammlung über das Dart Stream-Konzept bereit und ändert daher den Produkttyp im MyApp- und MyHomePage-Widget von Future <Liste <Produkt >> in Stream <QuerySnapshot>.

  • Ändern Sie die Erstellungsmethode des MyHomePage-Widgets so, dass StreamBuilder anstelle von FutureBuilder verwendet wird.

@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()); 
               }
            }, 
         ), 
      )
   ); 
}
  • Hier haben wir die Produktinformationen als List <DocumentSnapshot> -Typ abgerufen. Da unser Widget ProductBoxList nicht mit Dokumenten kompatibel ist, haben wir die Dokumente in den Typ List <Produkt> konvertiert und weiter verwendet.

  • Führen Sie abschließend die Anwendung aus und sehen Sie das Ergebnis. Da wir dieselben Produktinformationen wie die SQLite-Anwendung verwendet und nur das Speichermedium geändert haben, sieht die resultierende Anwendung identisch mit der SQLite- Anwendungsanwendung aus.