Flutter-database-concepts
Flutter-データベースの概念
Flutterは、データベースを操作するための多くの高度なパッケージを提供します。 最も重要なパッケージは-
- sqflite -SQLiteデータベースへのアクセスと操作に使用されます。
- firebase_database -Googleからクラウドホスト型NoSQLデータベースにアクセスして操作するために使用されます。
この章では、それぞれについて詳しく説明します。
SQLite
SQLiteデータベースは、事実上の標準のSQLベースの組み込みデータベースエンジンです。 小規模で実績のあるデータベースエンジンです。 sqfliteパッケージは、SQLiteデータベースを効率的に使用するための多くの機能を提供します。 SQLiteデータベースエンジンを操作する標準的な方法を提供します。 sqfliteパッケージによって提供されるコア機能は次のとおりです-
- SQLiteデータベースを作成/開く(openDatabaseメソッド)。
- SQLiteデータベースに対してSQLステートメント(実行メソッド)を実行します。
- SQLiteデータベースから情報を照会および取得するために必要なコードを削減するための高度な照会メソッド(照会メソッド)。
sqfliteパッケージを使用して標準SQLiteデータベースエンジンから製品情報を保存および取得する製品アプリケーションを作成し、SQLiteデータベースおよびsqfliteパッケージの背後にある概念を理解しましょう。
- Androidスタジオでproduct_sqlite_appに新しいFlutterアプリケーションを作成します。
- デフォルトの起動コード(main.dart)を_product_rest_app_コードに置き換えます。
- アセットフォルダーを_product_nav_app_から_product_rest_app_にコピーし、* 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
- 以下に示すようにpubspec.yamlファイルでsqfliteパッケージを構成します-
dependencies: sqflite: any
sqfliteの最新バージョン番号を代わりに使用します
- 以下に示すように、pubspec.yamlファイルでpath_providerパッケージを構成します-
dependencies: path_provider: any
- ここでは、path_providerパッケージを使用して、システムの一時フォルダーパスとアプリケーションのパスを取得します。 _any_の代わりに_sqflite_の最新バージョン番号を使用します。
- Androidスタジオは、pubspec.yamlが更新されたことを警告します。
- [依存関係の取得]オプションをクリックします。 Androidスタジオはインターネットからパッケージを取得し、アプリケーション用に適切に構成します。
- データベースでは、名前、価格などの製品プロパティとともに主キー、IDが追加フィールドとして必要なので、Productクラスにidプロパティを追加します。 また、新しいメソッドtoMapを追加して、製品オブジェクトをMapオブジェクトに変換します。 fromMapとtoMapは、Productオブジェクトのシリアル化と非シリアル化に使用され、データベース操作メソッドで使用されます。
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
};
}
- libフォルダーに新しいファイルDatabase.dartを作成して、_SQLite_関連機能を記述します。
- 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';
- ここで次の点に注意してください-
- async は非同期メソッドを記述するために使用されます。
- io はファイルとディレクトリへのアクセスに使用されます。
- path は、ファイルパスに関連するdartコアユーティリティ関数にアクセスするために使用されます。
- path_provider は、一時パスとアプリケーションパスを取得するために使用されます。
- sqflite は、SQLiteデータベースを操作するために使用されます。
- 新しいクラス SQLiteDbProvider を作成します
- 以下に指定されているように、シングルトンベースの静的SQLiteDbProviderオブジェクトを宣言します-
class SQLiteDbProvider {
SQLiteDbProvider._();
static final SQLiteDbProvider db = SQLiteDbProvider._();
static Database _database;
}
- SQLiteDBProvoiderオブジェクトとそのメソッドは、静的db変数を介してアクセスできます。
SQLiteDBProvoider.db.<emthod>
- タイプFuture <Database>のデータベース(Futureオプション)を取得するメソッドを作成します。 データベース自体の作成中に製品テーブルを作成し、初期データをロードします。
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"]
);
}
);
}
ここでは、次の方法を使用しました-
- getApplicationDocumentsDirectory -アプリケーションディレクトリパスを返します
- join -システム固有のパスを作成するために使用されます。 データベースパスの作成に使用しました。
- openDatabase -SQLiteデータベースを開くために使用
- onOpen -データベースを開くときにコードを書くために使用
- onCreate -データベースの初回作成時にコードを記述するために使用
- db.execute -SQLクエリの実行に使用されます。 クエリを受け入れます。 クエリにプレースホルダー(?)がある場合、2番目の引数のリストとして値を受け入れます。
データベース内のすべての製品を取得するメソッドを記述します-
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;
}
ここでは、次のことを行いました-
クエリメソッドを使用して、すべての製品情報を取得します。 queryは、クエリ全体を記述せずにテーブル情報をクエリするショートカットを提供します。 queryメソッドは、columns、orderByなどの入力を使用して適切なクエリ自体を生成します。
ProductのfromMapメソッドを使用して、テーブル内のすべての行を保持する結果オブジェクトをループすることにより、製品の詳細を取得しました。
*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;
}
- ここでは、whereおよびwhereArgsを使用してフィルターを適用しました。
- データベースから製品を挿入、更新、削除する3つのメソッド-insert、update、deleteメソッドを作成します。
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]);
}
- 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';
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]);
}
}
- mainメソッドを変更して、製品情報を取得します。
void main() {
runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts()));
}
- ここでは、getAllProductsメソッドを使用して、データベースからすべての製品をフェッチしました。
- アプリケーションを実行し、結果を確認します。 製品情報が保存され、ローカルSQLiteデータベースから取得されることを除いて、前の例_Accessing Product service API_に似ています。
クラウドファイヤーストア
FirebaseはBaaSアプリ開発プラットフォームです。 認証サービス、クラウドストレージなどのモバイルアプリケーション開発を高速化する多くの機能を提供します。Firebaseの主な機能の1つは、クラウドベースのリアルタイムNoSQLデータベースであるCloud Firestoreです。
Flutterは、Cloud Firestoreでプログラムするための特別なパッケージcloud_firestoreを提供します。 Cloud Firestoreでオンライン製品ストアを作成し、製品ストアにアクセスするためのアプリケーションを作成しましょう。
- Androidスタジオでproduct_firebase_appに新しいFlutterアプリケーションを作成します。
- デフォルトの起動コード(main.dart)を_product_rest_app_コードに置き換えます。
- Product.dartファイルをproduct_rest_appから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'],
);
}
}
- アセットフォルダーをproduct_rest_appからproduct_firebase_appにコピーし、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
- 以下に示すように、pubspec.yamlファイルでcloud_firestoreパッケージを構成します-
dependencies: cloud_firestore: ^0.9.13+1
- ここでは、cloud_firestoreパッケージの最新バージョンを使用します。
- Androidスタジオは、pubspec.yamlがここに示すように更新されたことを警告します-
[依存関係の取得]オプションをクリックします。 Androidスタジオはインターネットからパッケージを取得し、アプリケーション用に適切に構成します。
次の手順を使用してFirebaseでプロジェクトを作成します-
- https://firebase.google.com/pricing/。で無料プランを選択して、Firebaseアカウントを作成します。
- Firebaseアカウントが作成されると、プロジェクト概要ページにリダイレクトされます。 Firebaseベースのプロジェクトをすべてリストし、新しいプロジェクトを作成するオプションを提供します。
- [プロジェクトの追加]をクリックすると、プロジェクト作成ページが開きます。
- 製品名としてプロジェクト名を入力し、[プロジェクトの作成]オプションをクリックします。
- * Firebaseコンソールに移動します。
- [プロジェクトの概要]をクリックします。 プロジェクトの概要ページが開きます。
- Androidアイコンをクリックします。 Android開発に固有のプロジェクト設定が開きます。
- Androidパッケージ名com.finddevguides.flutterapp.product_firebase_appを入力します。
- アプリの登録をクリックします。 プロジェクト構成ファイルgoogle_service.jsonを生成します。
- google_service.jsonをダウンロードして、プロジェクトのandroid/appディレクトリに移動します。 このファイルは、アプリケーションとFirebase間の接続です。
- android/app/build.gradleを開き、次のコードを含めます-
apply plugin: 'com.google.gms.google-services'
- android/build.gradleを開き、次の構成を含めます-
buildscript {
repositories {
//...
}
dependencies {
//...
classpath 'com.google.gms:google-services:3.2.1'//new
}
}
ここでは、プラグインとクラスパスはgoogle_service.jsonファイルを読み取る目的で使用されます。
- android/app/build.gradleを開き、次のコードも含めます。
android {
defaultConfig {
...
multiDexEnabled true
}
...
}
dependencies {
...
compile 'com.android.support: multidex:1.0.3'
}
この依存関係により、Androidアプリケーションは複数のdex機能を使用できます。
- Firebase Consoleの残りの手順に従うか、単にスキップします。
次の手順を使用して、新しく作成されたプロジェクトに製品ストアを作成します-
- Firebaseコンソールに移動します。
- 新しく作成したプロジェクトを開きます。
- 左側のメニューで[データベース]オプションをクリックします。
- [データベースオプションの作成]をクリックします。
- [テストモードで開始]をクリックし、[有効にする]をクリックします。
- [コレクションを追加]をクリックします。 コレクション名として製品を入力し、[次へ]をクリックします。
- ここに画像に示すようにサンプル製品情報を入力してください-
- [ドキュメントの追加]オプションを使用して、追加の製品情報を追加します。
- main.dartファイルを開き、Cloud Firestoreプラグインファイルをインポートして、httpパッケージを削除します。
import 'package:cloud_firestore/cloud_firestore.dart';
- parseProductsを削除し、fetchProductsを更新して、製品サービスAPIではなくCloud Firestoreから製品を取得します。
Stream<QuerySnapshot> fetchProducts() {
return Firestore.instance.collection('product').snapshots(); }
- ここでは、Firestore.instance.collectionメソッドを使用して、クラウドストアで利用可能な製品コレクションにアクセスします。 Firestore.instance.collectionには、コレクションをフィルタリングして必要なドキュメントを取得するための多くのオプションが用意されています。 ただし、すべての製品情報を取得するためのフィルターは適用していません。
- Cloud FirestoreはDart Streamコンセプトを通じてコレクションを提供するため、MyAppおよびMyHomePageウィジェットの製品タイプをFuture <list <Product >>からStream <QuerySnapshot>に変更します。
- MyHomePageウィジェットのビルドメソッドを変更して、FutureBuilderではなくStreamBuilderを使用します。
@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());
}
},
),
)
);
}
- ここでは、List <DocumentSnapshot>タイプとして製品情報を取得しました。 ウィジェットのProductBoxListはドキュメントと互換性がないため、ドキュメントをList <Product>タイプに変換し、さらに使用しました。
- 最後に、アプリケーションを実行して結果を確認します。 _SQLite application_と同じ製品情報を使用し、記憶媒体のみを変更したため、結果のアプリケーションは_SQLite application_アプリケーションと同一に見えます。