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_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 스튜디오는 pubspec.yaml이 업데이트되었음을 ​​알립니다.

  • 종속성 가져 오기 옵션을 클릭하십시오. Android 스튜디오는 인터넷에서 패키지를 가져와 애플리케이션에 맞게 적절하게 구성합니다.

  • 데이터베이스에서는 이름, 가격 등과 같은 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 
   }; 
}
  • SQLite 관련 기능 을 작성하려면 lib 폴더에 새 파일 Database.dart를 만듭니다 .

  • Database.dart에서 필요한 import 문을 가져옵니다.

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 파일 경로와 관련된 다트 핵심 유틸리티 기능에 액세스하는 데 사용됩니다.

    • 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 쿼리를 실행하는 데 사용됩니다. 쿼리를받습니다. 쿼리에 자리 표시 자 (?)가있는 경우 두 번째 인수의 목록으로 값을 허용합니다.

  • 데이터베이스의 모든 제품을 얻는 방법을 작성하십시오-

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 메소드는 column, 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를 사용하여 필터를 적용했습니다.

  • 데이터베이스에서 제품을 삽입, 업데이트 및 삭제하는 세 가지 방법-삽입, 업데이트 및 삭제 방법을 만듭니다.

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]);
   } 
}
  • 제품 정보를 얻기 위해 주요 방법을 변경하십시오.

void main() {
   runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); 
}
  • 여기서는 getAllProducts 메소드를 사용하여 데이터베이스에서 모든 제품을 가져 왔습니다.

  • 응용 프로그램을 실행하고 결과를 확인합니다. 제품 정보가 로컬 SQLite 데이터베이스에서 저장되고 가져온다는 점을 제외하면 이전 예제 인 Accessing Product service API 와 유사 합니다.

Cloud Firestore

Firebase는 BaaS 앱 개발 플랫폼입니다. 인증 서비스, 클라우드 스토리지 등과 같은 모바일 애플리케이션 개발 속도를 높이기위한 많은 기능을 제공합니다. Firebase의 주요 기능 중 하나는 클라우드 기반 실시간 NoSQL 데이터베이스 인 Cloud Firestore입니다.

Flutter는 Cloud Firestore로 프로그래밍 할 수있는 특별한 패키지 인 cloud_firestore를 제공합니다. Cloud Firestore에 온라인 제품 스토어를 만들고 제품 스토어에 액세스 할 수있는 애플리케이션을 만들어 보겠습니다.

  • Android 스튜디오 product_firebase_app에서 새 Flutter 애플리케이션을 만듭니다.

  • 기본 시작 코드 (main.dart)를 product_rest_app 코드로 바꿉니다 .

  • product_rest_app의 Product.dart 파일을 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에서 프로젝트를 만듭니다.

    • 무료 요금제를 선택하여 Firebase 계정을 만듭니다. https://firebase.google.com/pricing/.

    • Firebase 계정이 생성되면 프로젝트 개요 페이지로 리디렉션됩니다. 모든 Firebase 기반 프로젝트를 나열하고 새 프로젝트를 만드는 옵션을 제공합니다.

    • 프로젝트 추가를 클릭하면 프로젝트 생성 페이지가 열립니다.

    • 프로젝트 이름으로 제품 앱 db를 입력하고 프로젝트 생성 옵션을 클릭합니다.

    • * Firebase 콘솔로 이동합니다.

    • 프로젝트 개요를 클릭합니다. 프로젝트 개요 페이지가 열립니다.

    • 안드로이드 아이콘을 클릭하세요. 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 콘솔의 나머지 단계를 따르거나 건너 뛰세요.

  • 다음 단계를 사용하여 새로 생성 된 프로젝트에 제품 스토어를 생성합니다.

    • 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>으로 수정합니다.

  • FutureBuilder 대신 StreamBuilder를 사용하도록 MyHomePage 위젯의 빌드 방법을 변경합니다.

@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 애플리케이션 애플리케이션 과 동일하게 보입니다 .