Flutter - conceitos de banco de dados

O Flutter oferece muitos pacotes avançados para trabalhar com bancos de dados. Os pacotes mais importantes são -

  • sqflite - Usado para acessar e manipular banco de dados SQLite, e

  • firebase_database - Usado para acessar e manipular o banco de dados NoSQL hospedado em nuvem do Google.

Neste capítulo, vamos discutir cada um deles em detalhes.

SQLite

O banco de dados SQLite é o mecanismo de banco de dados embutido baseado em SQL padrão e de fato. É um mecanismo de banco de dados pequeno e testado pelo tempo. O pacote sqflite fornece muitas funcionalidades para trabalhar de forma eficiente com o banco de dados SQLite. Ele fornece métodos padrão para manipular o mecanismo de banco de dados SQLite. A funcionalidade principal fornecida pelo pacote sqflite é a seguinte -

  • Crie / abra (método openDatabase) um banco de dados SQLite.

  • Execute a instrução SQL (método execute) no banco de dados SQLite.

  • Métodos de consulta avançados (método de consulta) para reduzir ao código necessário para consultar e obter informações do banco de dados SQLite.

Vamos criar um aplicativo de produto para armazenar e buscar informações de produto de um mecanismo de banco de dados SQLite padrão usando o pacote sqflite e entender o conceito por trás do banco de dados SQLite e do pacote sqflite.

  • Crie um novo aplicativo Flutter no Android Studio, product_sqlite_app.

  • Substitua o código de inicialização padrão (main.dart) por nosso código product_rest_app .

  • Copie a pasta de ativos de product_nav_app para product_rest_app e adicione ativos dentro do arquivo * 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
  • Configure o pacote sqflite no arquivo pubspec.yaml conforme mostrado abaixo -

dependencies: sqflite: any

Use o número da versão mais recente do sqflite no lugar de qualquer

  • Configure o pacote path_provider no arquivo pubspec.yaml conforme mostrado abaixo -

dependencies: path_provider: any
  • Aqui, o pacote path_provider é usado para obter o caminho da pasta temporária do sistema e o caminho do aplicativo. Use o número da versão mais recente de sqflite no lugar de qualquer .

  • Android Studio irá alertar que o pubspec.yaml está atualizado.

  • Clique na opção Obter dependências. O Android Studio obterá o pacote da Internet e o configurará corretamente para o aplicativo.

  • No banco de dados, precisamos da chave primária, id como campo adicional junto com as propriedades do produto como nome, preço, etc., então, adicione a propriedade id na classe Product. Além disso, adicione um novo método, toMap, para converter o objeto de produto em objeto de Mapa. fromMap e toMap são usados ​​para serializar e desserializar o objeto Produto e é usado em métodos de manipulação de banco de dados.

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 
   }; 
}
  • Crie um novo arquivo, Database.dart na pasta lib para escrever a funcionalidade relacionada ao SQLite .

  • Importe a instrução de importação necessária em 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';
  • Observe os seguintes pontos aqui -

    • async é usado para escrever métodos assíncronos.

    • io é usado para acessar arquivos e diretórios.

    • path é usado para acessar a função do utilitário central do dart relacionada aos caminhos de arquivo.

    • path_provider é usado para obter o caminho temporário e do aplicativo.

    • sqflite é usado para manipular banco de dados SQLite.

  • Crie uma nova classe SQLiteDbProvider

  • Declare um objeto SQLiteDbProvider estático baseado em singleton conforme especificado abaixo -

class SQLiteDbProvider { 
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
}
  • O objeto SQLiteDBProvoider e seu método podem ser acessados ​​por meio da variável db estática.

SQLiteDBProvoider.db.<emthod>
  • Crie um método para obter o banco de dados (opção Future) do tipo Future <Database>. Crie a tabela de produtos e carregue os dados iniciais durante a criação do próprio banco de dados.

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"]
         ); 
      }
   ); 
}
  • Aqui, usamos os seguintes métodos -

    • getApplicationDocumentsDirectory - Retorna o caminho do diretório do aplicativo

    • join- Usado para criar um caminho específico do sistema. Nós o usamos para criar o caminho do banco de dados.

    • openDatabase - Usado para abrir um banco de dados SQLite

    • onOpen - Usado para escrever código ao abrir um banco de dados

    • onCreate - Usado para escrever código enquanto um banco de dados é criado pela primeira vez

    • db.execute- Usado para executar consultas SQL. Ele aceita uma consulta. Se a consulta tiver espaço reservado (?), Ela aceitará os valores como lista no segundo argumento.

  • Escreva um método para obter todos os produtos no banco de dados -

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; 
}
  • Aqui, fizemos o seguinte -

    • Método de consulta usado para buscar todas as informações do produto. consulta fornece um atalho para consultar as informações de uma tabela sem escrever a consulta inteira. método de consulta irá gerar a própria consulta adequada usando nossa entrada como colunas, orderBy, etc.,

    • Método fromMap de Product usado para obter detalhes do produto por meio do loop do objeto de resultados, que contém todas as linhas da tabela.

  • Escreva um método para obter um produto específico para 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; 
}
  • Aqui, usamos where e whereArgs para aplicar filtros.

  • Crie três métodos - inserir, atualizar e excluir método para inserir, atualizar e excluir o produto do banco de dados.

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]); 
}
  • O código final do Database.dart é o seguinte -

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]);
   } 
}
  • Altere o método principal para obter as informações do produto.

void main() {
   runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); 
}
  • Aqui, usamos o método getAllProducts para buscar todos os produtos do banco de dados.

  • Execute o aplicativo e veja os resultados. Será semelhante ao exemplo anterior, Acessando a API de serviço do produto, exceto que as informações do produto são armazenadas e buscadas no banco de dados SQLite local.

Cloud Firestore

Firebase é uma plataforma de desenvolvimento de aplicativos BaaS. Ele oferece muitos recursos para acelerar o desenvolvimento de aplicativos móveis, como serviço de autenticação, armazenamento em nuvem, etc. Um dos principais recursos do Firebase é o Cloud Firestore, um banco de dados NoSQL em tempo real baseado em nuvem.

O Flutter fornece um pacote especial, cloud_firestore, para programar com o Cloud Firestore. Vamos criar uma loja de produtos online no Cloud Firestore e criar um aplicativo para acessar a loja de produtos.

  • Crie um novo aplicativo Flutter no Android Studio, product_firebase_app.

  • Substitua o código de inicialização padrão (main.dart) por nosso código product_rest_app .

  • Copie o arquivo Product.dart de product_rest_app para a pasta 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'], 
      ); 
   }
}
  • Copie a pasta de ativos de product_rest_app para product_firebase_app e inclua ativos dentro do arquivo 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
  • Configure o pacote cloud_firestore no arquivo pubspec.yaml conforme mostrado abaixo -

dependencies: cloud_firestore: ^0.9.13+1
  • Aqui, use a versão mais recente do pacote cloud_firestore.

  • O Android Studio irá alertar que o pubspec.yaml é atualizado conforme mostrado aqui -

  • Clique na opção Obter dependências. O Android Studio obterá o pacote da Internet e o configurará corretamente para o aplicativo.

  • Crie um projeto no Firebase usando as seguintes etapas -

    • Crie uma conta Firebase selecionando Plano gratuito em https://firebase.google.com/pricing/.

    • Depois que a conta do Firebase for criada, ela será redirecionada para a página de visão geral do projeto. Ele lista todos os projetos baseados no Firebase e oferece uma opção para criar um novo projeto.

    • Clique em Adicionar projeto e abrirá uma página de criação de projeto.

    • Insira products app db como o nome do projeto e clique na opção Criar projeto.

    • Acesse * Firebase console.

    • Clique em Visão geral do projeto. Ele abre a página de visão geral do projeto.

    • Clique no ícone do Android. Ele abrirá a configuração do projeto específica para o desenvolvimento Android.

    • Digite o nome do pacote Android, com.tutorialspoint.flutterapp.product_firebase_app.

    • Clique em Registrar aplicativo. Ele gera um arquivo de configuração de projeto, google_service.json.

    • Baixe google_service.json e mova-o para o diretório android / app do projeto. Este arquivo é a conexão entre nosso aplicativo e o Firebase.

    • Abra android / app / build.gradle e inclua o seguinte código -

apply plugin: 'com.google.gms.google-services'
    • Abra android / build.gradle e inclua a seguinte configuração -

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

    Aqui, o plug-in e o caminho da classe são usados ​​para ler o arquivo google_service.json.

    • Abra android / app / build.gradle e inclua o código a seguir também.

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

    Essa dependência permite que o aplicativo Android use várias funcionalidades dex.

    • Siga as etapas restantes no Firebase console ou simplesmente pule-as.

  • Crie uma loja de produtos no projeto recém-criado usando as seguintes etapas -

    • Acesse o console do Firebase.

    • Abra o projeto recém-criado.

    • Clique na opção Banco de dados no menu esquerdo.

    • Clique na opção Criar banco de dados.

    • Clique em Iniciar no modo de teste e depois em Ativar.

    • Clique em Adicionar coleção. Insira o produto como nome da coleção e clique em Avançar.

    • Insira as informações do produto de amostra conforme mostrado na imagem aqui -

  • Adicione informações adicionais sobre o produto usando Adicionar opções de documento .

  • Abra o arquivo main.dart, importe o arquivo de plug-in do Cloud Firestore e remova o pacote http.

import 'package:cloud_firestore/cloud_firestore.dart';
  • Remova parseProducts e atualize fetchProducts para buscar produtos do Cloud Firestore em vez da API de serviço do produto.

Stream<QuerySnapshot> fetchProducts() { 
   return Firestore.instance.collection('product').snapshots(); }
  • Aqui, o método Firestore.instance.collection é usado para acessar a coleção de produtos disponível no armazenamento em nuvem. Firestore.instance.collection fornece muitas opções para filtrar a coleção para obter os documentos necessários. Porém, não aplicamos nenhum filtro para obter todas as informações do produto.

  • O Cloud Firestore fornece a coleção por meio do conceito Dart Stream e, portanto, modifica o tipo de produtos no widget MyApp e MyHomePage de Future <list <Product>> para Stream <QuerySnapshot>.

  • Altere o método de construção do widget MyHomePage para usar StreamBuilder em vez de 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()); 
               }
            }, 
         ), 
      )
   ); 
}
  • Aqui, buscamos as informações do produto como o tipo List <DocumentSnapshot>. Como nosso widget, ProductBoxList não é compatível com documentos, convertemos os documentos para o tipo Lista <Produto> e o usamos posteriormente.

  • Finalmente, execute o aplicativo e veja o resultado. Desde então, usamos as mesmas informações do produto do aplicativo SQLite e alteramos apenas o meio de armazenamento, o aplicativo resultante parece idêntico ao aplicativo SQLite .