Flutter - концепции базы данных
Flutter предоставляет множество расширенных пакетов для работы с базами данных. Самые важные пакеты -
sqflite - Используется для доступа и управления базой данных SQLite, а также
firebase_database - Используется для доступа к базе данных NoSQL, размещенной в облаке, и управления ею от Google.
В этой главе давайте подробно обсудим каждый из них.
SQLite
База данных SQLite - это де-факто и стандартный встроенный механизм базы данных на основе SQL. Это небольшой и проверенный временем движок базы данных. Пакет sqflite предоставляет множество функций для эффективной работы с базой данных SQLite. Он предоставляет стандартные методы для управления ядром базы данных SQLite. Основные функции, предоставляемые пакетом sqflite, следующие:
Создать / открыть (метод openDatabase) базу данных SQLite.
Выполнить инструкцию SQL (метод выполнения) для базы данных SQLite.
Расширенные методы запроса (метод запроса) для сокращения кода, необходимого для запроса и получения информации из базы данных SQLite.
Давайте создадим приложение продукта для хранения и извлечения информации о продукте из стандартного механизма базы данных SQLite с использованием пакета sqflite и разберемся с концепцией базы данных SQLite и пакета sqflite.
Создайте новое приложение Flutter в студии Android, product_sqlite_app.
Замените код запуска по умолчанию (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
Настройте пакет sqflite в файле pubspec.yaml, как показано ниже -
dependencies: sqflite: any
Используйте последнюю версию sqflite вместо любых
Настройте пакет path_provider в файле pubspec.yaml, как показано ниже -
dependencies: path_provider: any
Здесь пакет path_provider используется для получения пути к временной папке системы и пути к приложению. Используйте номер последней версии sqflite вместо любого .
Студия Android сообщит, что pubspec.yaml обновлен.
Щелкните параметр Получить зависимости. Студия Android получит пакет из Интернета и правильно настроит его для приложения.
В базе данных нам нужен первичный ключ, id в качестве дополнительного поля вместе со свойствами продукта, такими как имя, цена и т. Д. Итак, добавьте свойство 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
};
}
Создайте новый файл Database.dart в папке lib для записи функций, связанных с 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) типа Future <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"]
);
}
);
}
Здесь мы использовали следующие методы -
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 предоставляет ярлык для запроса информации о таблице без написания всего запроса. 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 для применения фильтров.
Создайте три метода - вставить, обновить и удалить метод для вставки, обновления и удаления продукта из базы данных.
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 для получения всех продуктов из базы данных.
Запустите приложение и посмотрите результат. Он будет аналогичен предыдущему примеру Доступ к сервисному API продукта , за исключением того, что информация о продукте сохраняется и извлекается из локальной базы данных SQLite.
Cloud Firestore
Firebase - это платформа для разработки приложений BaaS. Он предоставляет множество функций для ускорения разработки мобильных приложений, таких как служба аутентификации, облачное хранилище и т. Д. Одной из основных функций Firebase является Cloud Firestore, облачная база данных NoSQL в реальном времени.
Flutter предоставляет специальный пакет cloud_firestore для программирования с Cloud Firestore. Давайте создадим интернет-магазин продуктов в Cloud Firestore и создадим приложение для доступа к магазину продуктов.
Создайте новое приложение Flutter в студии Android, 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
Настройте пакет cloud_firestore в файле pubspec.yaml, как показано ниже -
dependencies: cloud_firestore: ^0.9.13+1
Здесь используйте последнюю версию пакета cloud_firestore.
Студия Android сообщит, что pubspec.yaml обновлен, как показано здесь -
Щелкните параметр Получить зависимости. Студия Android получит пакет из Интернета и правильно настроит его для приложения.
Создайте проект в Firebase, выполнив следующие действия:
Создайте учетную запись Firebase, выбрав бесплатный план на https://firebase.google.com/pricing/.
После создания учетной записи Firebase она будет перенаправлена на страницу обзора проекта. В нем перечислены все проекты на основе Firebase и есть возможность создать новый проект.
Нажмите Добавить проект, и откроется страница создания проекта.
Введите базу данных приложения продуктов в качестве имени проекта и нажмите кнопку Создать проект.
Перейдите в * консоль 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
}
}
Откройте android / app / build.gradle и включите также следующий код.
Здесь подключаемый модуль и путь к классу используются для чтения файла google_service.json.
android {
defaultConfig {
...
multiDexEnabled true
}
...
}
dependencies {
...
compile 'com.android.support: multidex:1.0.3'
}
Выполните оставшиеся шаги в консоли Firebase или просто пропустите их.
Создайте магазин продуктов во вновь созданном проекте, выполнив следующие действия:
Перейдите в консоль Firebase.
Откройте только что созданный проект.
Щелкните опцию База данных в левом меню.
Нажмите кнопку «Создать базу данных».
Нажмите Пуск в тестовом режиме, а затем - Включить.
Щелкните Добавить коллекцию. Введите продукт в качестве названия коллекции и нажмите Далее.
Введите информацию об образце продукта, как показано на изображении здесь -
Эта зависимость позволяет приложению Android использовать несколько функций dex.
Добавьте дополнительную информацию о продукте с помощью параметров Добавить документ .
Откройте файл main.dart, импортируйте файл плагина Cloud Firestore и удалите пакет http.
import 'package:cloud_firestore/cloud_firestore.dart';
Удалите parseProducts и обновите fetchProducts, чтобы получать продукты из Cloud Firestore вместо API службы продуктов.
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, чтобы использовать StreamBuilder вместо 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());
}
},
),
)
);
}
Здесь мы получили информацию о продукте как тип List <DocumentSnapshot>. Поскольку наш виджет ProductBoxList не совместим с документами, мы преобразовали документы в тип List <Product> и в дальнейшем использовали его.
Наконец, запустите приложение и посмотрите результат. Поскольку мы использовали ту же информацию о продукте, что и приложение SQLite, и изменили только носитель данных, полученное приложение выглядит идентично приложению приложения SQLite .