运用 ISAR 数据库供给离线 Flutter 支撑
译文 medium.com/@tkarmakar2…
前语
这是我的口头禅,我试图遵从我的 应用 application 。关于那些针对二三线城市的面向客户的应用程序,应优先考虑离线支撑。
咱们可以运用像 SQLite 这样的联系数据库,也可以运用由 Hive、乃至 Isar 供给的非联系数据库。
在这个博客中,咱们将深化研究工作原理,了解 Isar 数据库的运用进程和易用性。
正文
什么是 Isar 数据库?
ISAR 数据库是一个超快速的跨平台 Flutter 数据库。
以下是 Isar 的一些特征和亮点,
- 为 Flutter 而生
- 高度可 extension
- 特征丰厚
- 支撑全文查找
- ACID 语义学
- 静态类型
- Something 异步
- 开放源码
施行
让咱们看看怎么可以轻松地实现在咱们的 Flutter 应用程序 Isar 数据库。
首先,咱们有必要了解咱们的应用程序应该可以执行什么。
故事时间。
关于本教程,咱们有一个药物库存应用程序,是由代表运用增加,删去和更新他们的药物库存。
假定该代表将拜访偏远地区出售这种药物,咱们有必要施行完整的离线支撑,使他可以执行一切的进程离线和数据得到自动更新,当有互联网衔接。
数据流
- 应用程序发动并查看数据。假如是,它从数据库中获取一切药物并存储在 Isar。假如没有,它将从 Isar 获取数据并填充数据。
- 保存在 ISAR 数据库中的数据包括一个 isSynces 标志,该标志表示数据与 firebase 同步的天气。
- 每逢一种新的药物被增加,假如有互联网,它会同时更新 Isar 和火力基地,不然它会更新 Isar 与 isSynced 标志为假。
- 编排也相同。
- 删去每个删去的项目将增加到一个列表中,并从 Isar 删去。一旦有了衔接,它就会更新数据库。
- 为了使数据一直坚持同步,每隔 30 秒运用一个定时器查看衔接状况,并且整个数据库与在线数据库同步。
咱们开端编程吧
我将假定您现已创立了该项目和集成的 Firebase,因而咱们有权拜访 Firebase 的 firestore。
让咱们集成网络查看器,以便让衔接状况一直存在。
为此咱们将运用,
- connectivity_plus
pub.dev/packages/co…
还有
- internet_connection_checker
pub.dev/packages/in…
main.dart , Flutter 代码,
// Import the firebase_core plugin
import 'dart:async';
import 'dart:developer';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:get/route_manager.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:medicine_inventory/controller/inventory_controller.dart';
import 'package:medicine_inventory/database/isar_helper.dart';
import 'package:medicine_inventory/screen/inventory_list_page.dart';
import 'package:provider/provider.dart';
ValueNotifier<bool> isDeviceConnected = ValueNotifier(false);
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const App());
}
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
late StreamSubscription<ConnectivityResult> subscription;
@override
void initState() {
IsarHelper.instance.init();
super.initState();
subscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
isDeviceConnected.value = await InternetConnectionChecker().hasConnection;
log("Internet status ====== $isDeviceConnected");
});
}
@override
void dispose() {
subscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => InventoryController(),
),
],
child: const GetMaterialApp(
home: InventoryListPage(),
),
);
}
}
//something went wrong
class SomethingWentWrong extends StatelessWidget {
const SomethingWentWrong({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('Something went wrong \nPlease try again',textAlign: TextAlign.center,),
),
);
}
}
这里,咱们正在创立和 valueListable 变量,每次连通性发生变化时,它都会发生变化。
读取数据
一旦应用程序发动,咱们将需求从在线或离线来历的药物清单。咱们将等候一秒钟,根据网络状况,从 firebase 获取数据并存储来自 Isar 的最新数据或负载。
//get inventory list
Future<void> getInventoryList({bool showLoading = true}) async {
showLoading ? startLoading() : null;
_inventoryList = [];
await Future.delayed(const Duration(seconds: 1)).then((value) async {
if (isDeviceConnected.value) {
_inventoryList = await _cloudFirestoreHelper.getAllMedicine();
IsarHelper.instance.insertFresh(_inventoryList);
} else {
_inventoryList = await IsarHelper.instance.getItems();
}
});
stopLoading();
notifyListeners();
}
增加数据
为了创立/增加药物,咱们将遵从相同的办法,咱们将创立对象并查看网络状况(假如存在) ,issync 将为 true,并将在 Isar 和 firebase 中保存。
void addInventory(Inventory inventory) async {
inventory.isSynced = isDeviceConnected.value;
int id = await IsarHelper.instance.insertOne(inventory);
inventory.id = id;
_inventoryList.add(inventory);
if (isDeviceConnected.value) {
_cloudFirestoreHelper.addMedicine(inventory.toJson());
}
notifyListeners();
}
更新数据
我更新了简单的更新机制,我只是更新数量和存储在 Isar 或消防基地。
void updateMedicine(int index) async {
_inventoryList[index].quantity = _inventoryList[index].quantity! + 1;
_inventoryList[index].isSynced = isDeviceConnected.value;
int id = await IsarHelper.instance.insertOne(_inventoryList[index]);
_inventoryList[index].id = id;
if (isDeviceConnected.value) {
_cloudFirestoreHelper.updateMedicine(_inventoryList[index]);
}
notifyListeners();
}
删去数据
在删去数据的状况下,咱们将删去的数据存储在一个列表中,以防没有衔接,一旦数据同步,列表就会被清除。
void removeMedicine(Inventory inventory) async {
inventory.isSynced = false;
await IsarHelper.instance.removeItem(inventory);
_inventoryList
.removeWhere((element) => element.code_value == inventory.code_value);
if (isDeviceConnected.value) {
_cloudFirestoreHelper.removeInventory(inventory);
} else {
deletedMedicines.add(inventory);
}
notifyListeners();
}
与 Firebase 同步
最后一件事是不时更新数据,以便坚持数据的最新性。
void checkIsSynced() async {
List<Inventory> unsyncedMedicines =
await IsarHelper.instance.getUnsyncedData();
if (deletedMedicines.isNotEmpty) {
for (Inventory element in deletedMedicines) {
_cloudFirestoreHelper.removeInventory(element);
}
deletedMedicines.clear();
}
if (unsyncedMedicines.isNotEmpty) {
for (Inventory element in unsyncedMedicines) {
element.isSynced = true;
await _cloudFirestoreHelper.updateMedicine(element);
IsarHelper.instance.updateSync(element);
}
}
getInventoryList(showLoading: false);
}
咱们现已完成了 CRUD 功能,现在咱们将看到咱们怎么在 Isar 数据库中做相同的工作。
首先,咱们创立了一个 helper singleton 类,
class IsarHelper {
IsarHelper._privateConstructor();
static final IsarHelper _instance = IsarHelper._privateConstructor();
static IsarHelper get instance => _instance;
late Isar isarInstance;
init() async {
isarInstance = await Isar.open([InventorySchema]);
}
}
view raw
一旦咱们创立了 singleton,咱们将翻开 Isar 数据库的一个实例并传递模式。
什么是 schema?
Schema 是在数据库中保存数据时遵从的模式。
创立模式,咱们将创立一个带有必需变量的模型。
import 'package:isar/isar.dart';
part 'inventory.g.dart';
@collection
class Inventory {
Id id = Isar.autoIncrement;
String? authorizedBy;
String? code_value;
String? description;
DateTime? expiryDate;
String? hospitalId;
String? mobile;
String? productName;
String? productType;
int? quantity;
String? status;
String? unitCost;
bool isSynced;
Inventory({
this.authorizedBy,
this.code_value,
this.description,
this.expiryDate,
this.hospitalId,
this.mobile,
this.productName,
this.productType,
this.quantity,
this.status,
this.unitCost,
this.isSynced = true,
});
factory Inventory.fromJson(json) {
return Inventory(
authorizedBy: json['authorizedBy'],
code_value: json['code_value'],
description: json['description'],
expiryDate: DateTime.parse(json['expiryDate']),
hospitalId: json['hospitalId'],
mobile: json['mobile'],
productName: json['productName'],
productType: json['productType'],
quantity: json['quantity'],
status: json['status'],
unitCost: json['unitCost'],
);
}
Map<String, dynamic> toJson() {
return {
"id": id,
'authorizedBy': authorizedBy,
'code_value': code_value,
'description': description,
'expiryDate': expiryDate!.toIso8601String(),
'hospitalId': hospitalId,
'mobile': mobile,
'productName': productName,
'productType': productType,
'quantity': quantity,
'status': status,
'unitCost': unitCost,
};
}
}
咱们需求运用@Collection 标记来构建模式。
咱们将运用 build_runner 包生成代码。
pub.dev/packages/bu…
接下来,咱们将看到一切 CRUD 函数。
假如你仔细阅览代码,你会发现,关于写入 Isar 数据库,咱们正在将整个事务包装在一个事务中,以便顺畅地进行更改。这是 Isar 文件所主张的。
InventorySheme 是由生成器函数创立的。
让咱们看看代码,
import 'package:isar/isar.dart';
import 'package:medicine_inventory/model/inventory.dart';
class IsarHelper {
IsarHelper._privateConstructor();
static final IsarHelper _instance = IsarHelper._privateConstructor();
static IsarHelper get instance => _instance;
late Isar isarInstance;
init() async {
isarInstance = await Isar.open([InventorySchema]);
}
insertFresh(List<Inventory> inventoryList) async {
await isarInstance.writeTxn(() async {
await isarInstance.clear();
for (Inventory element in inventoryList) {
await isarInstance.inventorys.put(element);
}
});
}
insertOne(Inventory inventoryItem) async {
late int id;
await isarInstance.writeTxn(() async {
id = await isarInstance.inventorys.put(inventoryItem);
});
return id;
}
getItems() async {
IsarCollection<Inventory> medicineCollection =
isarInstance.collection<Inventory>();
List<Inventory?> medicines = await medicineCollection.where().findAll();
return medicines;
}
removeItem(Inventory inventory) async {
await isarInstance.writeTxn(() async {
await isarInstance.inventorys.delete(inventory.id);
});
}
void updateSync(Inventory inventory) async {
inventory.isSynced = true;
await isarInstance.writeTxn(() async {
await isarInstance.inventorys.put(inventory);
});
}
getUnsyncedData() async {
IsarCollection<Inventory> medicineCollection =
isarInstance.collection<Inventory>();
List<Inventory?> medicines =
await medicineCollection.filter().isSyncedEqualTo(false).findAll();
return medicines;
}
}
假如你现已走了这么远,
恭喜你运用 Isar 理解了离线数据库运用的大部分概念。
结束语
假如本文对你有协助,请转发让更多的朋友阅览。
或许这个操作只需你 3 秒钟,对我来说是一个鼓励,感谢。
祝你有一个美好的一天~
猫哥
-
微信 ducafecat
-
wiki.ducafecat.tech
-
video.ducafecat.tech
本文由mdnice多平台发布