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
}
}
Otwórz android / app / build.gradle i dołącz również następujący kod.
Tutaj wtyczka i ścieżka klasy są używane do odczytu pliku google_service.json.
android {
defaultConfig {
...
multiDexEnabled true
}
...
}
dependencies {
...
compile 'com.android.support: multidex:1.0.3'
}
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 -
Ta zależność umożliwia aplikacji na Androida korzystanie z wielu funkcji dex.
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 .