- 颤振教程
- 颤动 - 主页
- 颤振 - 简介
- 颤振 - 安装
- 在 Android Studio 中创建简单的应用程序
- Flutter - 架构应用
- Dart 编程简介
- Flutter - Widget 简介
- Flutter - 布局简介
- Flutter - 手势简介
- Flutter - 状态管理
- 颤动 - 动画
- Flutter - 编写 Android 特定代码
- Flutter - 编写 IOS 特定代码
- Flutter - 包简介
- Flutter - 访问 REST API
- Flutter - 数据库概念
- Flutter - 国际化
- 颤振 - 测试
- Flutter - 部署
- Flutter - 开发工具
- Flutter - 编写高级应用程序
- 颤动 - 结论
- 颤动有用的资源
- Flutter - 快速指南
- Flutter - 有用的资源
- Flutter - 讨论
Flutter - 数据库概念
Flutter 提供了许多高级包来处理数据库。最重要的包是 -
sqflite - 用于访问和操作 SQLite 数据库,以及
firebase_database - 用于访问和操作来自 Google 的云托管 NoSQL 数据库。
在本章中,让我们详细讨论它们中的每一个。
SQLite
SQLite 数据库是事实上的、标准的基于 SQL 的嵌入式数据库引擎。它是小型且经过时间考验的数据库引擎。sqflite 包提供了许多功能来有效地使用 SQLite 数据库。它提供了操作 SQLite 数据库引擎的标准方法。sqflite 包提供的核心功能如下 -
创建/打开(openDatabase 方法)一个 SQLite 数据库。
针对 SQLite 数据库执行 SQL 语句(execute 方法)。
高级查询方法(query method)减少从 SQLite 数据库查询和获取信息所需的代码。
让我们创建一个产品应用程序,以使用 sqflite 包从标准 SQLite 数据库引擎存储和获取产品信息,并了解 SQLite 数据库和 sqflite 包背后的概念。
在 Android studio 中创建一个新的 Flutter 应用程序,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
在 pubspec.yaml 文件中配置 sqflite 包,如下所示 -
dependencies: sqflite: any
使用 sqflite 的最新版本号代替任何
在 pubspec.yaml 文件中配置 path_provider 包,如下所示 -
dependencies: path_provider: any
这里,path_provider包用于获取系统的临时文件夹路径和应用程序的路径。使用sqflite的最新版本号代替任何.
Android studio 将提醒 pubspec.yaml 已更新。
单击获取依赖项选项。Android studio 将从互联网获取包并为应用程序正确配置它。
在数据库中,我们需要主键、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 core 实用函数。
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 提供了查询表信息的快捷方式,而无需编写整个查询。query 方法将使用我们的输入(如列、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]); } }
更改main方法来获取产品信息。
void main() { runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); }
在这里,我们使用 getAllProducts 方法从数据库中获取所有产品。
运行应用程序并查看结果。它与前面的示例“访问产品服务 API”类似,不同之处在于产品信息是从本地 SQLite 数据库存储和获取的。
云Firestore
Firebase 是一个 BaaS 应用开发平台。它提供了许多功能来加速移动应用程序的开发,如身份验证服务、云存储等,Firebase 的主要功能之一是 Cloud Firestore,一个基于云的实时 NoSQL 数据库。
Flutter 提供了一个特殊的包 cloud_firestore 来使用 Cloud Firestore 进行编程。让我们在 Cloud Firestore 中创建一个在线产品商店,并创建一个应用程序来访问该产品商店。
在 Android studio 中创建一个新的 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 中创建一个项目 -
通过在 https://firebase.google.com/pricing/ 选择免费计划来创建 Firebase 帐户。
创建 Firebase 帐户后,它将重定向到项目概述页面。它列出了所有基于 Firebase 的项目,并提供了创建新项目的选项。
单击“添加项目”,将打开项目创建页面。
输入 products app 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 } }
打开 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 应用程序完全相同。