Flutter - pojęcia związane z bazą danych

Flutter zapewnia wiele zaawansowanych pakietów do pracy z bazami danych. Najważniejsze pakiety to -

  • sqflite - Służy do uzyskiwania dostępu do bazy danych SQLite i manipulowania nią oraz

  • firebase_database - Służy do uzyskiwania dostępu do hostowanej w chmurze bazy danych NoSQL od Google i manipulowania nią.

W tym rozdziale omówimy szczegółowo każdy z nich.

SQLite

Baza danych SQLite jest de facto i standardowym wbudowanym silnikiem bazy danych opartym na języku SQL. Jest to mały i sprawdzony silnik bazy danych. Pakiet sqflite zapewnia wiele funkcji do wydajnej pracy z bazą danych SQLite. Zapewnia standardowe metody manipulowania silnikiem bazy danych SQLite. Podstawowe funkcje zapewniane przez pakiet sqflite są następujące -

  • Utwórz / Otwórz (metoda openDatabase) bazę danych SQLite.

  • Wykonaj instrukcję SQL (metoda wykonania) w bazie danych SQLite.

  • Zaawansowane metody zapytań (metoda zapytań) w celu zredukowania kodu wymaganego do zapytań i uzyskania informacji z bazy danych SQLite

Stwórzmy aplikację produktu do przechowywania i pobierania informacji o produkcie ze standardowego silnika bazy danych SQLite przy użyciu pakietu sqflite i poznajmy koncepcję bazy danych SQLite i pakietu sqflite.

  • Utwórz nową aplikację Flutter w Android Studio, product_sqlite_app.

  • Zastąp domyślny kod startowy (main.dart) naszym kodem product_rest_app .

  • Skopiuj folder asset z product_nav_app do product_rest_app i dodaj zasoby w pliku * 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
  • Skonfiguruj pakiet sqflite w pliku pubspec.yaml, jak pokazano poniżej -

dependencies: sqflite: any

Użyj najnowszego numeru wersji sqflite zamiast dowolnego

  • Skonfiguruj pakiet path_provider w pliku pubspec.yaml, jak pokazano poniżej -

dependencies: path_provider: any
  • Tutaj pakiet path_provider służy do uzyskania tymczasowej ścieżki folderu systemu i ścieżki aplikacji. Użyj najnowszego numeru wersji sqflite zamiast dowolnego .

  • Studio Android zaalarmuje, że plik pubspec.yaml został zaktualizowany.

  • Kliknij opcję Pobierz zależności. Android studio pobierze pakiet z Internetu i odpowiednio skonfiguruje go dla aplikacji.

  • W bazie danych potrzebujemy klucza podstawowego, id jako dodatkowego pola wraz z właściwościami produktu, takimi jak nazwa, cena itp., Więc dodaj właściwość id w klasie Product. Dodaj również nową metodę toMap do konwersji obiektu produktu na obiekt Map. fromMap i toMap są używane do serializacji i deserializacji obiektu Product i są używane w metodach manipulacji bazami danych.

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 
   }; 
}
  • Utwórz nowy plik Database.dart w folderze lib, aby zapisać funkcje związane z SQLite .

  • Zaimportuj niezbędną instrukcję importu w 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';
  • Zwróć uwagę na następujące punkty -

    • async służy do pisania metod asynchronicznych.

    • io służy do uzyskiwania dostępu do plików i katalogów.

    • path służy do uzyskiwania dostępu do podstawowych funkcji narzędziowych dart związanych ze ścieżkami plików.

    • path_provider służy do uzyskania ścieżki tymczasowej i aplikacji.

    • sqflite służy do manipulowania bazą danych SQLite.

  • Utwórz nową klasę SQLiteDbProvider

  • Zadeklaruj pojedynczy, statyczny obiekt SQLiteDbProvider, jak określono poniżej -

class SQLiteDbProvider { 
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
}
  • Dostęp do obiektu SQLiteDBProvoider i jego metody można uzyskać za pośrednictwem statycznej zmiennej db.

SQLiteDBProvoider.db.<emthod>
  • Utwórz metodę pobierania bazy danych (opcja Future) typu Future <Database>. Utwórz tabelę produktów i załaduj dane początkowe podczas tworzenia samej bazy danych.

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"]
         ); 
      }
   ); 
}
  • Tutaj zastosowaliśmy następujące metody -

    • getApplicationDocumentsDirectory - Zwraca ścieżkę do katalogu aplikacji

    • join- Służy do tworzenia ścieżki specyficznej dla systemu. Użyliśmy go do stworzenia ścieżki do bazy danych.

    • openDatabase - Służy do otwierania bazy danych SQLite

    • onOpen - Służy do pisania kodu podczas otwierania bazy danych

    • onCreate - Służy do pisania kodu podczas tworzenia bazy danych po raz pierwszy

    • db.execute- Służy do wykonywania zapytań SQL. Przyjmuje zapytanie. Jeśli zapytanie ma symbol zastępczy (?), To przyjmuje wartości jako listę w drugim argumencie.

  • Napisz metodę, aby uzyskać wszystkie produkty w bazie danych -

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; 
}
  • Tutaj wykonaliśmy następujące czynności -

    • Użyto metody zapytania, aby pobrać wszystkie informacje o produkcie. zapytanie zapewnia skrót do odpytywania informacji z tabeli bez pisania całego zapytania. metoda zapytania sama wygeneruje odpowiednie zapytanie, korzystając z naszych danych wejściowych, takich jak kolumny, orderBy itp.,

    • Zastosowano metodę fromMap produktu Product, aby uzyskać szczegółowe informacje o produkcie, zapętlając obiekt wyników, który przechowuje wszystkie wiersze w tabeli.

  • Napisz metodę, dla której zostanie określony produkt 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; 
}
  • Tutaj użyliśmy gdzie i gdzieArgs do zastosowania filtrów.

  • Utwórz trzy metody - wstaw, zaktualizuj i usuń metodę wstawiania, aktualizowania i usuwania produktu z bazy danych.

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]); 
}
  • Ostateczny kod Database.dart wygląda następująco -

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]);
   } 
}
  • Zmień główną metodę uzyskiwania informacji o produkcie.

void main() {
   runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); 
}
  • Tutaj użyliśmy metody getAllProducts, aby pobrać wszystkie produkty z bazy danych.

  • Uruchom aplikację i zobacz wyniki. Będzie podobny do poprzedniego przykładu, Accessing Product service API , z wyjątkiem tego, że informacje o produkcie są przechowywane i pobierane z lokalnej bazy danych SQLite.

Cloud Firestore

Firebase to platforma do tworzenia aplikacji BaaS. Zapewnia wiele funkcji przyspieszających tworzenie aplikacji mobilnych, takich jak usługa uwierzytelniania, przechowywanie w chmurze itp. Jedną z głównych funkcji Firebase jest Cloud Firestore, oparta na chmurze baza danych NoSQL w czasie rzeczywistym.

Flutter zapewnia specjalny pakiet cloud_firestore do programowania w Cloud Firestore. Stwórzmy internetowy sklep z produktami w Cloud Firestore i stwórzmy aplikację umożliwiającą dostęp do sklepu z produktami.

  • Utwórz nową aplikację Flutter w Android Studio, product_firebase_app.

  • Zastąp domyślny kod startowy (main.dart) naszym kodem product_rest_app .

  • Skopiuj plik Product.dart z produktu product_rest_app do folderu 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'], 
      ); 
   }
}
  • Skopiuj folder zasobów z produktu product_rest_app do produktu_firebase_app i dodaj zasoby w pliku 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
  • Skonfiguruj pakiet cloud_firestore w pliku pubspec.yaml, jak pokazano poniżej -

dependencies: cloud_firestore: ^0.9.13+1
  • Tutaj użyj najnowszej wersji pakietu cloud_firestore.

  • Studio Android ostrzeże, że plik pubspec.yaml został zaktualizowany, jak pokazano tutaj -

  • Kliknij opcję Pobierz zależności. Android studio pobierze pakiet z Internetu i odpowiednio skonfiguruje go dla aplikacji.

  • Utwórz projekt w Firebase, wykonując następujące czynności -

    • Utwórz konto Firebase, wybierając Abonament bezpłatny pod adresem https://firebase.google.com/pricing/.

    • Po utworzeniu konta Firebase nastąpi przekierowanie do strony przeglądu projektu. Zawiera listę wszystkich projektów opartych na Firebase i zapewnia opcję utworzenia nowego projektu.

    • Kliknij Dodaj projekt, aby otworzyć stronę tworzenia projektu.

    • Wpisz bazę danych aplikacji produktów jako nazwę projektu i kliknij opcję Utwórz projekt.

    • Przejdź do konsoli * Firebase.

    • Kliknij Przegląd projektu. Otwiera stronę przeglądu projektu.

    • Kliknij ikonę Androida. Otworzy ustawienia projektu specyficzne dla programowania na Androida.

    • Wpisz nazwę pakietu Androida, com.tutorialspoint.flutterapp.product_firebase_app.

    • Kliknij Zarejestruj aplikację. Generuje plik konfiguracyjny projektu google_service.json.

    • Pobierz google_service.json, a następnie przenieś go do katalogu android / app projektu. Ten plik jest połączeniem między naszą aplikacją a Firebase.

    • Otwórz android / app / build.gradle i dołącz następujący kod -

apply plugin: 'com.google.gms.google-services'
    • Otwórz android / build.gradle i dołącz następującą konfigurację -

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

    Tutaj wtyczka i ścieżka klasy są używane do odczytu pliku google_service.json.

    • Otwórz android / app / build.gradle i dołącz również następujący kod.

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

    Ta zależność umożliwia aplikacji na Androida korzystanie z wielu funkcji dex.

    • Wykonaj pozostałe kroki w konsoli Firebase lub po prostu je pomiń.

  • Utwórz sklep z produktami w nowo utworzonym projekcie, wykonując następujące kroki -

    • Przejdź do konsoli Firebase.

    • Otwórz nowo utworzony projekt.

    • Kliknij opcję Baza danych w menu po lewej stronie.

    • Kliknij opcję Utwórz bazę danych.

    • Kliknij Uruchom w trybie testowym, a następnie Włącz.

    • Kliknij Dodaj kolekcję. Wprowadź produkt jako nazwę kolekcji, a następnie kliknij Dalej.

    • Wprowadź przykładowe informacje o produkcie, jak pokazano na obrazku tutaj -

  • Dodaj dodatkowe informacje o produkcie za pomocą opcji Dodaj dokument .

  • Otwórz plik main.dart i zaimportuj plik wtyczki Cloud Firestore oraz usuń pakiet http.

import 'package:cloud_firestore/cloud_firestore.dart';
  • Usuń parseProducts i zaktualizuj fetchProducts, aby pobierać produkty z Cloud Firestore zamiast z interfejsu Product Service API.

Stream<QuerySnapshot> fetchProducts() { 
   return Firestore.instance.collection('product').snapshots(); }
  • Tutaj metoda Firestore.instance.collection służy do uzyskania dostępu do kolekcji produktów dostępnych w sklepie w chmurze. Firestore.instance.collection zapewnia wiele opcji filtrowania kolekcji w celu uzyskania niezbędnych dokumentów. Ale nie zastosowaliśmy żadnego filtra, aby uzyskać wszystkie informacje o produkcie.

  • Cloud Firestore udostępnia kolekcję za pomocą koncepcji Dart Stream, a więc zmodyfikuj typ produktu w widżecie MyApp i MyHomePage z Future <list <Product>> do Stream <QuerySnapshot>.

  • Zmień metodę kompilacji widżetu MyHomePage, aby używał StreamBuilder zamiast 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()); 
               }
            }, 
         ), 
      )
   ); 
}
  • Tutaj pobraliśmy informacje o produkcie jako typ List <DocumentSnapshot>. Ponieważ nasz widżet ProductBoxList nie jest zgodny z dokumentami, przekonwertowaliśmy dokumenty na typ List <Product> i dalej go używaliśmy.

  • Na koniec uruchom aplikację i zobacz wynik. Ponieważ użyliśmy tych samych informacji o produkcie, co w aplikacji SQLite i zmieniliśmy tylko nośnik pamięci, wynikowa aplikacja wygląda identycznie jak aplikacja SQLite .