使用 ISAR 数据库提供离线 Flutter 支持

news2024/11/16 13:56:52

使用 ISAR 数据库提供离线 Flutter 支持

alt

译文 https://medium.com/@tkarmakar27112000/flutter-offline-support-using-isar-database-b79f5006d3a

前言

这是我的口头禅,我试图遵循我的 应用 application 。对于那些针对二三线城市的面向客户的应用程序,应优先考虑离线支持。

我们可以使用像 SQLite 这样的关系数据库,也可以使用由 Hive、甚至 Isar 提供的非关系数据库。

在这个博客中,我们将深入研究工作原理,了解 Isar 数据库的使用过程和易用性。

正文

什么是 Isar 数据库?

ISAR 数据库是一个超快速的跨平台 Flutter 数据库。

alt

以下是 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/connectivity_plus

还有

  • internet_connection_checker

https://pub.dev/packages/internet_connection_checker

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<Stringdynamic> 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/build_runner

接下来,我们将看到所有 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 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Model Fusion via Optimal Transport论文阅读+代码解析

论文地址点这里 一. 论文基本介绍 最近2023ICLR中的一篇论文被曝抄袭一事&#xff0c;而进行举报的作者就是本次要将的论文的作者之一&#xff0c;可以发现本篇论文的工作是非常不错的。本篇论文也是第一个从最优运输地角度考虑模型之间地融合技术&#xff0c;通过排列神经元…

Snort搭建以及规则编写

目录 Snort IDS Snort搭建 安装web服务&#xff0c;方便设置sonrt后期访问 Mysql安装 PHP安装 安装 Snort Snort配置 创建snort专用的用户和组 配置目录 配置规则 修改配置文件 规则编写 Snort IDS Snort IDS&#xff08;入侵检测系统&#xff09;是一个强大的网…

Airtest自定义启动器支持批量运行脚本,并兼容在AirtestIDE中使用

小编注&#xff1a;上期详细讲了Airtest启动器的原理&#xff0c;以及在最后给出了2个实现方案。本次是第2个方案的另一个实现案例&#xff0c;供大家学习参考。 Python v3.7.0 / Airtest: 1.1.1 / PocoUI: 1.0.78 自定义的启动器主要实现了以下功能&#xff1a; 将一些公共…

浙大医疗健康产业管理MBA提面经验分享

各位潜在的学弟学妹们好&#xff0c;很高兴和各位分享下我参加2022年浙大医疗产业管理MBA的提前批面试经验。在经过材料的撰写提交、面试备考各环节后顺利拿到优秀资格&#xff0c;为后面的笔试备考减轻了很大压力&#xff0c;回忆起去年的面试过程&#xff0c;我的面试以及备考…

沉睡者IT - 为你解密那些卖虚拟资源和知识付费课程的平台到底有多简单和多赚钱。

潜力博主推荐&#xff0c;点击上面关注博主 ↑ ↑ 上图为平台首页面截图&#xff0c;官方总站演示&#xff1a;vip.zzzz.la 备用演示&#xff1a;VIP.网站 1.虚拟资源平台介绍&#xff01; &#xff08;1&#xff09;虚拟资源项目站是一个在线知识付费平台&#xff0c;全自动…

Nacos 中的配置文件如何实现加密传输

小伙伴们知道&#xff0c;Spring Cloud Config 很早就提供了配置文件的加解密功能&#xff0c;并且支持对称加密和非对称加密两种不同的模式。Nacos 作为分布式配置中心服务注册中心的合体&#xff0c;在配置文件加密这块一直差点意思&#xff0c;不过好在&#xff0c;如果你使…

公众号免费搜题系统调用方法

公众号免费搜题系统调用方法 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xf…

SQL Server 服务的启动

目录 前言&#xff1a; 一、进入控制面板 二、开启 SQL Server 服务 1. 找到管理工具并点击 2. 双击服务 3. 找到SQL Server 数据库服务 4. 右键点击 SQL Server 服务&#xff0c;选择启动 三、修改 SQL Server 服务的启动方式 1. 右键点击服务&#xff0c;点击属性 …

激发客户潜在需求

企业不光要看到客户的显现需求&#xff0c;更要挖掘客户的潜在需求&#xff0c;因为客户的潜在需求是可以转化为显现需求的&#xff0c;满足客户的潜在需求可以为企业带来更多经济效益。 前言 潜在需求是指消费者虽然有明确意识的欲望&#xff0c;但由于种种原因还没有明确的显…

Redis的缓存更新策略和缓存问题

1.缓存更新 1.1缓存更新策略 内存淘汰&#xff1a; 不需要自己维护&#xff0c;利用Redis的内存淘汰机制&#xff0c;当内存不足时自动淘汰部分数据&#xff0c;下次查询时更新缓存一致性 &#xff1a; 差维护成本&#xff1a;无 超时删除&#xff1a; 给缓存数据添加TTL时间…

零基础自学javase黑马课程第十五天

零基础自学javase黑马课程第十五天 ✨欢迎关注&#x1f5b1;点赞&#x1f380;收藏⭐留言✒ &#x1f52e;本文由京与旧铺原创&#xff0c;csdn首发&#xff01; &#x1f618;系列专栏&#xff1a;java学习 &#x1f4bb;首发时间&#xff1a;&#x1f39e;2022年11月21日&…

【案例设计】配置与批量化处理外部 Texture 导入格式转换

开发平台&#xff1a;Unity 2020 版本以上 编程平台&#xff1a;Visual Studio 2020 版本 编程语言&#xff1a;CSharp   前言 Unity 开发者不仅是要求在面对开发需求上有着预见性的目光与能力去应对各种功能实现。更加注重的是通过各个项目的开发类型与过程&#xff0c;总结…

计算机体系结构:不同改进方案的性价比计算

题目内容 某一计算机用于商业外贸的事务处理&#xff0c;有大量的字符串操作。由于这种事务处理很普遍&#xff0c;有较大的市场&#xff0c;故而设计人员决定在下一代此类计算机的CPU中加入字符串操作的功能。经测试应用软件调查发现&#xff0c;字符串操作的使用占整个程序运…

进程切换及一些常见概念(面试必问)

目录前言一、竞争性1、什么是进程的竞争性&#xff1f;2、为什么进程间存在竞争性&#xff1f;二、独立性#这里先简单了解三、并行四、并发五、优先级队列六、进程切换寄存器1. 函数返回值2. 进程上下文数据总结前言 在不同的进程在处理机上切换的过程中&#xff0c;我们需要学…

碳酸钙/GPC3单克隆抗体介导阿霉素二氧化硅纳米粒/DOX-GNRs@mSiO2-HA-RGD纳米制备方法

小编在这里整理了碳酸钙/GPC3单克隆抗体介导阿霉素二氧化硅纳米粒/DOX-GNRsmSiO2-HA-RGD纳米制备方法&#xff0c;来看&#xff01; 碳酸钙阿霉素二氧化硅纳米颗粒制备方法&#xff1a; 包括以下步骤&#xff1a; 将含有钙离子的乙醇溶液与含有氨水与盐酸阿霉素的水溶液混合,…

【Jupyter】远程连接Jupyter服务器

远程连接Jupyter 步骤一 配置Jupyter https://blog.csdn.net/MYRLibra/article/details/109599531 https://blog.csdn.net/weixin_40641725/article/details/114636779 安装 conda activate abc #激活虚拟环境 pip install jupyter #安装 jupyter notebook --generate-conf…

Webservice接口-WSDL文档【Webservice】

WSDL是一个用于精确描述Web服务的文档&#xff0c;WSDL文档是一个遵循WSDL-XML模式的XML文档。WSDL 文档将Web服务定义为服务访问点或端口的集合。在 WSDL 中&#xff0c;由于服务访问点和消息的抽象定义已从具体的服务部署或数据格式绑定中分离出来&#xff0c;因此可以对抽象…

python使用flask实现前后端分离通过前端修改数据库数据【全栈开发基础】

文章目录&#x1f68e;前言&#xff1a;&#x1f6fa;工具&#x1f693;截图&#x1f695;数据库截图&#x1f699;前端截图&#x1f698;代码&#x1f6b2;增加&#x1f355;前端 HTML&#x1f35f;后端 python&#x1f6f4; 删除&#x1f355;前端 HTML&#x1f35f;后端 pyt…

AMM 套利者

AMM 套利者 理由 以太坊和其他支持 EVM 的区块链上有很多 AMM。其中许多 AMM 是 UniswapV2 的分叉项目或与 UniswapV2 具有相同的接口。这些 AMM 的列表&#xff1a; Uniswap V2(以太坊)寿司交换&#xff08;以太坊&#xff09;煎饼掉期(BSC)MDEX(BSC/heco) ... 一旦相同代币…

SpiderPool - 云原生容器网络 IPAM 插件

SpiderPool 来源于容器网络落地实践的经验积累&#xff0c;是「Daocloud 道客」开源的原生容器网络 IPAM 插件&#xff08;github&#xff1a;https://github.com/spidernet-io/spiderpool&#xff09;&#xff0c;主要用于搭配 Underlay CNI 实现容器云平台精细化的管理和分配…