Flutter 离线数据方案 Flutter_Data 包

原文 https://levelup.gitconnected.com/flutter-offline-first-with-flutter-data-62bad61097be

前言

通过离线优先来改善您的用户体验

Flutter Data 是一个让你的应用程序先离线的软件包。

离线时,它在设备上使用 Hive 存储,当到 API 的连接可用时,更改将自动同步。

在本文中,我们将添加 flutter_data 包并将其连接起来,以便在应用程序启动时检索 recipes。

正文

我花了一段时间才弄清楚自己想要什么,并勾勒出设计草图:

High level design

High level design

当时发生了很多事情,要想做好是很复杂的。

我用粗体文本突出显示了一些需要编码或配置的操作,并用它们制作了一个任务列表:

  • 步骤 1: 在启动时从 app_config.json 加载环境值。
  • 步骤 2: 添加新包。
  • 步骤 3: 注释 recipes 模型。
  • 步骤 4: 配置 Flutter 数据。
  • 步骤 5: 覆盖 Http 客户端使用 Dio。
  • 步骤 6: 为开发和测试配置 Mock Adapter。

您可以在 XP 部分中找到实现细节。

这篇文章有很多细节,我的建议是阅读足够多的内容来理解正在发生的事情,如果您决定或需要实现它,可以将其作为参考。

请反馈,如果你有更好的方法或建议的改进。我们欢迎任何有助于改进这一关键发展任务的帮助。

Ta Da 哒哒

GlobalEnvironmentValues.instance
   .loadValues(await rootBundle.loadString("app_config.json"));

Load the environment values from app_config.json on startup.

启动时从 app_config.json 加载环境值。

Using the mocked API client for requests

对请求使用模拟 API 客户端

Flutter Data triggering FindAll method for recipes

Flutter 数据触发 FindAll 方法

Display the recipes returned

显示返回的 recipes

Displaying recipes returned by Flutter Data

显示 Flutter Data 返回的 recipes

感觉有点虎头蛇尾,因为我们刚刚配置了离线访问,但仍在使用模拟数据进行开发和测试。

但是,这是我们现在可以支持新实体的离线数据的一大步,我们已经为关键的开发任务(添加或修改特性)准备了强大的编码模式。

一旦我进行了身份验证,我就会回来,使 Flutter Data 能够使用基于环境的云 API 服务,我希望设置 dev_online 和 dev_local 模式,并使用部署构建来配置 UAT 和 Live 版本。

实施细节

  • 步骤 1 ー在启动时加载环境值

Load environment values from app_config.json file

从 app_config.json 文件加载环境值

“GlobalEnvironmentValues”服务使用“ EnvConfigReader”加载 app_config.json 值:

import 'dart:convert';


// Access to enviromental values that are loaded from a config file.

// The CI/CD injects the per environment files on deployment.

class EnvConfigReader {

  late Map<String, dynamic> _config;


  EnvConfigReader(String configFileJson) {

    _config = json.decode(configFileJson) as Map<String, dynamic>;

  }


  String value(String key) {

    return _config[key];

  }

}
  • 步骤 2 ーー添加新的软件包
# Offline-first data framework with a customizable REST client and powerful model relationships.

flutter_data: ^1.4.7


# A reactive state caching and data-binding framework

# Recommend replacement for provider: that is mentioned by flutter

# https://docs.flutter.dev/development/data-and-backend/state-mgmt/options

hooks_riverpod: ^1.0.3+1


# Annotations used by code gen to create code for JSON serialization and deserialization

json_annotation: ^4.7.0


# Generate to/from JSON code for a class

json_serializable: ^6.5.4

步骤 3 ーー注释 recipes 模型

import 'package:flutter_data/flutter_data.dart';

import 'package:json_annotation/json_annotation.dart';

import 'package:simbucore/app/mixin/flutter_data_dev_api_server_adapter.dart';


part 'recipe.g.dart';


()

([FlutterDataApiServerAdapter])

class Recipe extends DataModel<Recipe> {

  

  final int? id;

  final String title;

  final String link;

  final String source;

  final String totalTime;

  final HasMany<Ingredient> ingredients;

  final HasMany<Step> steps;

  final String thumbnail;

将零件引用添加到将要生成的文件的导入,在下一步运行构建器之前,您将看到一个错误:

Missing file error

文件丢失错误

关系需要使用 HasMany 和 BelongsTo 查看更多信息的文档。

Mixin FlutterDataApiServerAdapter 使用我们现在在启动时加载的环境值设置 API 基 URL。

  • 步骤 4 ー设定 Flutter 数据

生成配方库

flutter pub run build_runner build

我们需要生成一个存储库,然后才能在启动时初始化它,因为代码生成会在它生成的文件中生成初始化方法:

File generated by Flutter Data (Hive)

由 Flutter 数据生成的文件(Hive)

现在我们可以在应用程序启动时将其连接起来:

Wire up the local Hive storage

Wire up the local Hive storage

连接本地的 Hive 仓库

  • 步骤 X ー模拟 API 回应

这个重新设计的步骤 X ーー Mock API Response,现在取代了计划中的两个步骤:

  • 步骤 5 ーー重写 Http 客户端以使用 Dio
  • 步骤 6 ー配置模拟适配器

在对 Flutter Data 代码进行了更深入的研究之后,很明显,尝试使用 Dio 将意味着与包对抗,很多好处都在于它使用的针对 watch One、 Save、 findAll 等方法的代码中。

通过研究 Flutter Data 如何测试它拥有的代码并决定重写 HttpClient,我找到了我需要的东西:

Overide HttpClient provider to mock API responses

重写 HttpClient 提供程序以模拟 API 响应

Dio 还会带来其他一些好处,但是看起来 Flutter Data 可以通过自定义适配器轻松 extension ,以处理身份验证令牌和重试之类的事情。在 Flutter Data 网站上有一些文章和代码示例。

为什么我选择使用 Flutter_Data

使用包是简化和减少项目代码的一种很好的方法,使其更容易维护。

我们在引入微应用程序 DigestablePrologue 时看到了这一点,它允许我删除主项目 DigestableMe 中的横切代码。

这简化了 DigestableMe 中的代码,让它专注于应用程序的特性,而 DigestablePrologue 处理所有的启动操作,比如布局和主题选择,以及连接 Riverpod 的 state management 包。

但是没有什么是 free 的,软件包会限制你,它们可能会停滞不前,或者会导致破坏性的变化,有依赖性,把你绑定到旧的软件包,有 bug 或者漏洞百出的抽象。

我的引爆流行是,Flutter_data 提供了大量我正在寻找的功能,与 Riverpod 非常契合:

  • 使用代码生成从带注释的类创建 CRUD 存储库。
  • 建立本地数据库。
  • 离线检测。
  • 反应式数据绑定。
  • 失败和重试处理。
  • 在线时与 RestAPI 自动同步。
  • 可交换的关系。
  • 和 Riverpod 一起工作。
  • 支持我想使用的 JSON: API。
  • JSON 序列化被加入进来。
  • 选项用于实现不可变性。

这是我们可以抽象出来的很多东西,我们可以创建一个新的规程来展示如何通过代码生成将新条目引入到应用程序中。

固执己见可以成为团队中真正的力量,如何达成共识是困难的一点,它通常涉及到更密切的合作。

多年来,我发现结对编程在组建团队和开始项目时确实很有帮助。

Going in the same direction

Going in the same direction

朝着同一个方向

每个环境的 API’s

将文件添加到项目 app_config 的根目录。Json 来保存开发 api 端点地址,这将被 CodeMagic 构建来支持其他环境所吞噬,详情请参阅他们的文章《带有 CodeMagic CD 的 Flutter 环境》。

然后拦截这个虚构的端点,指向我们的模拟开发数据。

Development environment config values

开发环境配置值

这些值可以通过提供程序访问,提供程序在应用程序运行时读取这些值。

灵活的学习和实验

在上一篇文章中,我添加了一个特性来检测客户机设备何时脱机,目的是使用它来同步脱机数据。

当我在本文中介绍 Flutter_Data 时,我意识到它可以处理检测,并且可以节省一些时间。然而:

你不能把未来的点点滴滴联系起来,你只能把它们回顾地联系起来... ..。

史蒂夫 · 乔布斯

它只是提醒我去尝试,但不要太珍惜扔掉我花时间在上面的工作,当一个更好的选择出现的时候。

Flutter Data ーー 关系

构建器强制要求图表中的所有相关模型都要注释:

Builder error - missing related ingredients model

生成器错误ーー缺少相关成分模型

Flutter Data ーー IsOfflineError 标志

How Flutter Data decides if a device is offline.

Flutter Data 如何决定设备是否脱机。

Hive 本地数据库文件

Hive 在这里编写本地数据库文件:

/Users/<user>/Library/Developer/CoreSimulator/Devices
/060DB285-2820-411B-B5F7-76F9739DA934/data/Containers/Data/Application
/A72C3081-43DD-4E03-82D0-DC49A3AF1317/Documents/flutter_data

基本目录是通过路径提供程序包中的 getApplicationDocumentsDirectory 函数获得的,您可以在生成的 main.data.dart 文件中找到代码。

...
if (!kIsWeb) {
    baseDirFn ??= () => getApplicationDocumentsDirectory()
    .then(
      (dir) => dir.path
      );
  } else {
    baseDirFn ??= () => '';
  }
...

如果在 configureRepositoryLocalStorage 初始化方法中重写 baseDirFn 参数,则可以设置自己的基目录。

进入 external 包代码

为了找到 Hive 没有写入权限的目录路径,我必须打开调试包代码的能力

Code.Preferences.Settings menu item

Code.Preferences.Settings 菜单项

我重新启动了 VisualStudio 代码以使其正常工作,但您可能只需要重新启动应用程序。

Flutter Data 数据诊断

我希望能够打开诊断日志记录,输出 Flutter Data 正在采取的关键操作,比如从 Hive 检索,因为离线并输出任何异常的信息,特别是从/到 json 的映射。

Discipline & Scaffold 脚手架

一旦我已经验证和使用 Flutter 数据的行动几次,我将创建一个新的纪律,围绕它和改善脚手架。

这将是大多数新特性的核心部分,值得努力使过程简单并且代码可维护。

Latency and Chat 延迟和聊天

如果我们遇到了响应问题,我们可能需要考虑减少对 API 的调用(少聊天) ,我怀疑这只是通过一个更大的图表或批处理调用,我们需要找出最好的方式,这将适合 flutter_data 包。

包和依赖项

在介绍了 Flutter_data 软件包之后,我试图将 Riverpod 升级到 2.1.1 版本,得到了以下结果:

Dependency conflict whilst upgrading Riverpod

依赖冲突同时升级 Riverpod

随着添加更多的软件包和微应用程序软件包,您的依赖关系将变得更难处理和升级。

这是一个候选的一些额外的报告或工具,以帮助我们的规模。

在这种情况下,Flutter_Data 需要 Riverpod 的 1.x 版本。

我在 pubspec.yaml 中为 Riverpod 指定了一些更高的版本号,在 simbucore 包中指定了 Flutter_Data,这简化了错误消息,从而解决了这个问题。

Clearer message

Clearer message

运行命令:

flutter pub outdated

显示可以解析的版本:

command output, showing the version that can be resolved

命令输出,显示可以解析的版本

Packages

结束语

如果本文对你有帮助,请转发让更多的朋友阅读。

也许这个操作只要你 3 秒钟,对我来说是一个激励,感谢。

祝你有一个美好的一天~

猫哥课程


© 猫哥

  • 微信 ducafecat

  • https://wiki.ducafecat.tech

  • https://ducafecat.com

Last Updated:
Contributors: ducafecat