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

Posted 会煮咖啡的猫咪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 ISAR 数据库提供离线 Flutter 支持相关的知识,希望对你有一定的参考价值。

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

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

前言

这是我的口头禅,我试图遵循我的 应用 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/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 = trueasync 
    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 多平台发布

以上是关于使用 ISAR 数据库提供离线 Flutter 支持的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 离线数据方案 Flutter_Data 包

如何使用 Isar 证明消除规则?

Flutter - 让应用在设备离线时显示之前获取的数据

使用flutter firebase在线/离线的用户存在

如何在 Flutter 中直接持久化存储 JSON 数据以供查询和离线使用?

如何创建 Flutter 应用的离线和在线