应用 ISAR 数据库提供离线 Flutter 反对

译文 https://medium.com/@tkarmakar...

前言

这是我的口头禅,我试图遵循我的 利用 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

https://pub.dev/packages/conn...

还有

  • internet_connection_checker

https://pub.dev/packages/inte...

main.dart , Flutter 代码,

// Import the firebase_core pluginimport '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 wrongclass 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';@collectionclass 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 包生成代码。

https://pub.dev/packages/buil...

接下来,咱们将看到所有 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
  • https://wiki.ducafecat.tech
  • https://video.ducafecat.tech

本文由mdnice多平台公布