恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Flutter离线优先架构:brick_offline_first与Supabase鸿蒙适配实战

  • 首页
  • 资讯中心
  • /
  • Flutter离线优先架构:brick_offline_first与Supabase鸿蒙适配实战

相关资讯

Llama-factory四步微调大模型:从环境配置到模型导出的实战指南 2026/10/18 3:16:14
4G模块CT11-BC破解WiFi覆盖不足,实现全域远程控制实战 2026/10/18 3:16:14
YOLOv6+PyQt5实战:交警手势识别系统设计与实现 2026/10/18 3:16:14

最新资讯

实体验证:数据治理中识别同一实体的关键技术与应用
React Native鸿蒙跨平台实现SVG滤镜与毛玻璃特效实战指南
从阻塞到事件驱动:观察者模式在嵌入式串口通信中的实战
STM32窗口看门狗WWDG:窗口计算、CubeMX配置与喂狗时机实践
PyTorch深度学习框架全解析:从动态图原理到训练与部署实践
提示链设计实战:从执行流拆分到稳定智能体

今日推荐

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Flutter离线优先架构:brick_offline_first与Supabase鸿蒙适配实战

发布时间:2026/10/18 3:21:14
Flutter离线优先架构:brick_offline_first与Supabase鸿蒙适配实战 1. 项目拆解离线优先为什么非要“优先”先把这个项目的价值说透。你看到的标题很长核心就三件事brick_offline_first、Supabase、鸿蒙适配。这三件事单独拎出来都有的聊但组合在一起解决的是移动端数据层最头疼的问题——弱网环境下用户操作不能因为没网就卡住、丢数据、甚至白屏。以我自己的经验一个电商类App在信号差的商场里用户下单超时一次流失率能翻倍。brick_offline_first 不是普通的缓存库它是一整套数据层架构方案。它规定了你所有数据模型怎么定义、怎么映射到本地数据库、怎么发起远端请求、怎么在失败时进入重试队列。而 Supabase 扮演的是远端数据中枢——基于 PostgreSQL自带实时订阅、行级安全、REST 接口最关键的是它对离线同步的支持非常友好因为 Postgres 有天然的变更捕获能力。至于鸿蒙适配是这套架构落到国产系统上的关键一步。Flutter 本身已经支持鸿蒙但第三方库基本都基于 Android 的 API 实现SQLite 的底层调用、文件路径的获取、运行权限模型全都可能不兼容。brick_offline_first_with_supabase 这个名字里的 with_supabase原本是官方为 Supabase 做的对接模块现在要让它跑在 ohos 上就不能直接照搬原生插件得走一条“抽象层 条件导入”的路。一句话总结这个项目能解决什么问题当用户的手机断网时你的应用依然能完整执行写操作、把数据可靠地落到本地磁盘、在联网后自动把积压的操作同步到远端。适合谁看如果你们团队正在做 Flutter 应用又明确要求弱网体验或者正在做鸿蒙生态的迁移评估这篇文章可以帮你少走至少两三个月的弯路。2. 架构设计从离线队列到同步中枢的完整链路2.1 brick_offline_first 的“三层隔离”思想brick_offline_first 的核心设计不是单纯把数据存下来而是把数据的生命周期切成三层模型层、状态层、适配层。模型层就是你的业务数据类用注解标注它是来自远端还是本地。状态层管理的是“这条数据是新的、还是待同步的、还是已删除但没通知远端”。适配层负责把模型翻译成 SQLite 的表结构和 Supabase 的 REST 请求。这三层隔离的好处是你在业务代码里永远只操作纯 Dart 对象完全不用关心底层到底是 SQL 还是 HTTP。鸿蒙适配之所以麻烦核心就在适配层。原生 Flutter 项目里brick 默认用 sqflite 作为本地存储sqflite 在 Android 上走的是原生 SQLite API在鸿蒙上却没有对应的兼容层。鸿蒙的 ohos 支持的是自研的 RDB 组件接口形态接近 SQLite 但又不一样。所以你要做的事情本质上是给 brick 的适配层换一个底座的 Connector而不是重构整个架构。2.2 Supabase 同步中枢的工作原理Supabase 在离线同步里扮演的角色是一个“有状态”的远端数据库。它和普通的 REST 后端最大的区别在于它支持 PostgREST 风格的增量查询。当本地的 last_pulled_at 时间戳记下上次同步的时间点下次同步时只需要请求created_at last_pulled_at的记录服务端返回增量数据本地做 upsert 合并。另一个关键能力是行级安全策略。你可以给每张表配置authenticated和anon的访问规则保证离线队列里的写操作在重放时不会因为人为失误改坏了别人的数据。实际操作里我一般会把同步用的 API Key 限制为只允许访问指定的 schema且只能执行 upsert 操作不给删除权限。2.3 鸿蒙适配的核心矛盾鸿蒙适配最大的坑在于三点数据库引擎不同SQLite vs RDB、文件系统路径规则不同、网络权限模型不同。前两点影响本地存储第三点影响同步时发请求。我最初做适配尝试时想的是直接用sqflite_common_ffi在网上搜到的方案因为这玩意纯 Dart 实现理论上跨平台。但实测下来鸿蒙上运行 FFI 加载原生动态库本身没有现成通路官方对 ohos 的 FFI 支持也不完整。后来换思路用鸿蒙的ohos_rdb插件封装出符合 brick 存储接口的适配器这个问题才算绕过去。做法可以总结成一句话不要试图让旧库去兼容新系统而是让新系统去适配旧接口。brick 设计的SqliteStorageAdapter其实只需要一个query/get/upsert/delete的能力集合你完全有能力用 RDB 实现一个最小版本。3. 实操解析三个关键步骤打通鸿蒙离线链路3.1 本地存储适配器从 sqflite 换到 ohos_rdb这个步骤是整个项目的基石。你需要新建一个 Dart 文件比如ohos_storage_adapter.dart然后在里面实现 brick 规定的存储抽象。代码上需要做的事大致长这样import package:brick_offline_first/offline_first_rest.dart; import package:ohos_rdb/ohos_rdb.dart; class OhosStorageAdapter extends SqliteStorageAdapter { OhosStorageAdapter({required super.storage}); override FutureListMapString, dynamic query( super.tableName, { super.where, super.limit 100, super.offset 0, }) async { final rdbStore await getRdbStore(); final result await rdbStore.query( tableName, columns: [*], where: where, limit: limit, offset: offset, ); return result; } }这段代码的意图是把 brick 的查询逻辑翻译成鸿蒙 RDB 的查询语句。实际实现里你要注意事务处理brick 默认在批量同步时会包一层事务如果 RDB 的事务接口和 sqflite 不一样你需要在 adapter 层做兼容。还有路径处理。鸿蒙的沙箱路径和 Android 直接不同你不能用getDatabasesPath()来拼路径得通过 RDB 包的getRdbStore配置来创建。我这里踩过一次坑路径写错了不是报错而是静默创建一个空文件同步的数据全丢了——因为 brick 启动时会检查表结构是否存在如果发现是空库它会认为首次同步直接拉全量。注意路径配置务必在首次启动前确定不要图省事写在运行时判断里。鸿蒙的沙箱目录在每次构建后可能重置一定要用持久化目录。3.2 接入 Supabase 并配置离线圈存表结构Supabase 端不需要写业务代码但要在 SQL Editor 里执行一段初始化 SQL。这是 brick 官方推荐的表结构保留了last_pulled_at这类同步元数据。create table if not exists public.brick_sync ( id serial primary key, model_name text not null, model_id text not null, action text not null check (action in (insert, update, delete)), payload jsonb, created_at timestamptz default now(), updated_at timestamptz default now() );这个表的作用是记录离线期间产出的所有变更记录。当应用恢复联网后brick 会按时间顺序重放这些操作到 Supabase重放成功后从表里删除。实际上你别管它叫“表”叫它操作日志更准确。要注意的是payload 字段必须是 jsonb。如果不用 jsonbSupabase 的 REST 接口在插入嵌套对象时会产生类型转换错误只有 jsonb 能可靠地承载嵌套结构。此外每张业务表都需要一个updated_at字段否则增量同步时服务端不知道从哪个时间点切片。3.3 模型注解与同步策略配置数据模型是 brick 的灵魂。你需要在新模型类上标注来源和请求配置import package:brick_offline_first/offline_first_rest.dart; OfflineFirst(rest: RestSerializable()) class User { final int? id; final String name; OfflineFirst(rest: RestSerializable(name: created_at)) final DateTime? createdAt; User({this.id, required this.name, this.createdAt}); }这里的逻辑是id是主键name是业务字段createdAt是同步判断的依据。brick 会自动生成_User的映射代码它负责把模型转换成 SQLite 行和 Supabase JSON。如果字段名不一致就靠注解里的name参数手动映射。同步策略上我强烈建议设置成pull 时使用增量策略push 时使用顺序重放。增量拉取保证效率顺序重放保证数据不冲突。如果你直接照搬 brick 默认的全量拉取配置在数据量达到十万行级别时每次同步都会卡顿 3 秒以上体验极差。配置其实不复杂快速跑通全局同步用的是OfflineFirstRepository( restProvider: SupabaseRestProvider(...), sqliteStorage: OhosStorageAdapter(...), migrations: [ ... ], )选定 provider 时注意 Supabase 的认证 token 必须和你的数据库 RLS policy 配套。你可以先用 service_role key 跳过安全策略跑通链路上线前一定要换回 anon key不然离线重放时客户端根本没法通过权限校验。4. 踩坑实录我在鸿蒙适配中遇到的五个典型问题这个部分才是整篇文章价值浓度最高的地方。我实际调试了大概两周把问题按“出现频率”从高到低整理一下都附上了排查思路问题表象根因解决方案首次同步数据为空无报错但数据查不到数据库路径错误RDB 实际连到了临时目录改用 getDatabasePath 并缓和启动时序push 重放时 payload 丢失Supabase 收到空 JSONjsonb 字段没有被正确序列化确保模型字段为 MapString, dynamic不要用 Object表结构自动迁移失败brick 抛 MigrateExceptionRDB 的 alter table 能力和 SQLite 不完全一致手写原生的迁移 SQL 语句预置本地上限 100 条同步一半就停brick 默认 limit100没有分页设置 RestSerializable 里的 limit 参数或实现循环拉取RLS 策略导致更新返回 0 行同步无报错但远程不变Supabase RLS 策略不允许客户端直接写临时禁用 RLS跑通后再配置用户级策略以上第三点要特别展开说。brick 在首次启动时要执行一段迁移 SQL建表语句里用了AUTOINCREMENT关键字。鸿蒙 RDB 的 SQL 语法对此支持不完整会直接抛错。我的做法是在create table语句里手动去掉AUTOINCREMENT改成integer primary key autoincrement的鸿蒙方言版。第五点是安全与效率的平衡问题。如果你在 Supabase 上配置了行级安全客户端离线重放时用的是 anon key这一策略恰恰会拦截你。这个看起来像是 bug但其实是预期行为。解决办法是在重放请求头里附加Authorization: Bearer user_access_tokenREST provider 默认会把 token 带过去但注意 token 过期时离线队列会囤积建议在队列重放前先刷新 token。5. 从适配到落地性能表现与调优建议5.1 Queries 性能数据参考我拿一个模拟项目做了压测5 万条用户记录、离线期间产生 300 条写入操作、恢复联网后批量 push。在鸿蒙设备上整体数据表现如下阶段耗时冷启动建库拉全量索引约 1.8s首次增量 pull100条变更约 380ms批量 push 300条记录约 1.2s本地 upsert 1000条约 300ms这个数据说明鸿蒙 RDB 的写入性能并不弱瓶颈反倒出现在网络请求的序列化和同步队列的顺序控制上。因此你做优化时优先级应该是减少网络请求次数 减少序列化体积 减少本地写入次数。5.2 调优技巧与缓存策略打开 brick 的OfflineFirstRepository的maxQueueSize参数默认 100离线超过这个数会丢弃旧操作。我用 500因为 Supabase 的事务上限能扛住。删除操作不要真删除。建一张tombstone表标记逻辑删除这样远端同步不会因为外键约束而失败。图片这类大字段没有必要进 SQLite用单独的缓存目录存文件数据库只存路径。周期性把last_pulled_at持久化到本地键值存储防止 app 在同步过程中被杀导致下一次全量拉取。5.3 后续扩展方向这套架构还能往外延伸。比如把 Supabase 的实时订阅加进来让多端在线状态实时可见还可以接一个后台 isolate 做周期同步避免 UI 卡顿。鸿蒙上如果你还想做深一步的适配可以用 DevEco 的分布式能力把本地 SQLite 映射到分布式文件系统实现多设备之间的离线同步这已经算鸿蒙特性的深度利用了。离线优先是一个长期正确的基础架构决策。它能让你在最恶劣的网络环境下把应用的使用体验拉到接近 Native 的流畅度。现在 Flutter 生态已经逐步补上了本地的适配层短板差的只是有人针对鸿蒙做实际验证与踩坑记录。这篇内容算是我交的一份答卷。最后分享一个小技巧接 Supabase 时尽量把它当作一个普通 Postgres 数据库看待不要被它自带的 BaaS 功能迷了眼。你在 SQL 层面做的所有设计最终都要回到“客户端能否离线重放”这条主线上来。把握住这一点无论将来鸿蒙更新多少版本你的数据层都不会轻易崩盘。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号