Flutter-データベースの概念

Flutterは、データベースを操作するための多くの高度なパッケージを提供します。最も重要なパッケージは次のとおりです。

  • sqflite − SQLiteデータベースへのアクセスと操作に使用され、

  • firebase_database −GoogleからクラウドでホストされているNoSQLデータベースにアクセスして操作するために使用されます。

この章では、それぞれについて詳しく説明します。

SQLite

SQLiteデータベースは、事実上の標準のSQLベースの組み込みデータベースエンジンです。これは、小型で実績のあるデータベースエンジンです。sqfliteパッケージは、SQLiteデータベースと効率的に連携するための多くの機能を提供します。SQLiteデータベースエンジンを操作するための標準的な方法を提供します。sqfliteパッケージが提供するコア機能は次のとおりです。

  • SQLiteデータベースを作成/開く(openDatabaseメソッド)。

  • SQLiteデータベースに対してSQLステートメント(executeメソッド)を実行します。

  • SQLiteデータベースから情報をクエリして取得するために必要なコードに削減するための高度なクエリメソッド(クエリメソッド)。

sqfliteパッケージを使用して標準のSQLiteデータベースエンジンから製品情報を保存およびフェッチする製品アプリケーションを作成し、SQLiteデータベースとsqfliteパッケージの背後にある概念を理解しましょう。

  • AndroidStudioで新しいFlutterアプリケーションproduct_sqlite_appを作成します。

  • デフォルトのスタートアップコード(main.dart)をproduct_rest_appコードに置き換えます。

  • フォルダの資産をコピーproduct_nav_appproduct_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パッケージを使用して、システムの一時フォルダーパスとアプリケーションのパスを取得します。最新のバージョン番号を使用しsqfliteの代わりに任意の

  • Android Studioは、pubspec.yamlが更新されたことを警告します。

  • [依存関係の取得]オプションをクリックします。Android Studioはインターネットからパッケージを取得し、アプリケーション用に適切に構成します。

  • データベースでは、名前、価格などのProductプロパティとともに、追加フィールドとして主キー、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; 
}
  • ここでは、whereArgsとwhereArgsを使用してフィルターを適用しました。

  • データベースから製品を挿入、更新、削除するための挿入、更新、削除の3つのメソッドを作成します。

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データベースに保存およびフェッチされることを除いて、前の例である製品サービスAPIへのアクセスと同様です。

Cloud Firestore

FirebaseはBaaSアプリ開発プラットフォームです。認証サービスやクラウドストレージなど、モバイルアプリケーションの開発をスピードアップするための多くの機能を提供します。Firebaseの主な機能の1つは、クラウドベースのリアルタイムNoSQLデータベースであるCloudFirestoreです。

Flutterは、CloudFirestoreでプログラムするための特別なパッケージcloud_firestoreを提供します。Cloud Firestoreにオンライン製品ストアを作成し、製品ストアにアクセスするためのアプリケーションを作成しましょう。

  • AndroidStudioで新しいFlutterアプリケーションproduct_firebase_appを作成します。

  • デフォルトのスタートアップコード(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 Studioは、pubspec.yamlが次のように更新されたことを警告します-

  • [依存関係の取得]オプションをクリックします。Android Studioはインターネットからパッケージを取得し、アプリケーション用に適切に構成します。

  • 次の手順を使用して、Firebaseでプロジェクトを作成します-

    • で無料プランを選択してFirebaseアカウントを作成します https://firebase.google.com/pricing/.

    • Firebaseアカウントが作成されると、プロジェクトの概要ページにリダイレクトされます。Firebaseベースのすべてのプロジェクトが一覧表示され、新しいプロジェクトを作成するためのオプションが提供されます。

    • [プロジェクトの追加]をクリックすると、プロジェクト作成ページが開きます。

    • プロジェクト名としてproductsapp dbと入力し、[プロジェクトの作成]オプションをクリックします。

    • * Firebaseコンソールに移動します。

    • [プロジェクトの概要]をクリックします。プロジェクト概要ページが開きます。

    • Androidアイコンをクリックします。Android開発に固有のプロジェクト設定を開きます。

    • Androidパッケージ名com.tutorialspoint.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を更新して、Product serviceAPIではなくCloudFirestoreから製品をフェッチします。

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アプリケーションと同じ製品情報を使用し、記憶媒体のみを変更したため、結果のアプリケーションはSQLiteアプリケーションアプリケーションと同じように見えます。