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
}
}
Abra android / app / build.gradle e inclua o código a seguir também.
Aqui, o plug-in e o caminho da classe são usados para ler o arquivo google_service.json.
android {
defaultConfig {
...
multiDexEnabled true
}
...
}
dependencies {
...
compile 'com.android.support: multidex:1.0.3'
}
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 -
Essa dependência permite que o aplicativo Android use várias funcionalidades dex.
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 .