查看: 310|回复: 3

HarmonyOS Flutter数据模型优化:用Equatable告别

[复制链接]
发表于 昨天 13:00 | 显示全部楼层 |阅读模式
在HarmonyOS上跑Flutter项目,数据模型越写越多,每个类都得手动写 == 和 hashCode。虽然Dart的Object.hash一行搞定hashCode,但 == 那部分得逐个字段写比较,漏一个就是bug。Equatable这个纯Dart库,完美解决了这个问题。

手工写 == 的痛点
项目里用户模型四个字段,手动写operator==要八行,hashCode一行。但项目不只一个模型:订单模型十几个字段,地址模型七八个,商品模型五六个……每个都得来一套。有一次写订单模型的 ==,漏了shippingAddress字段,导致两个地址不同但其他字段相同的订单被判定相等,查了半天才发现是equals的问题。Dart没有Lombok,急需轻量级方案自动生成这些样板代码。

Equatable的核心思路
Equatable的做法很简单:继承Equatable类,通过props getter告诉它要比较哪些字段,==、hashCode、toString就全自动了。
  1. class User extends Equatable {
  2.   final String name;
  3.   final int age;
  4.   final String email;
  5.   final String phone;
  6.   const User(this.name, this.age, this.email, this.phone);
  7.   @override
  8.   List<Object?> get props => [name, age, email, phone];
  9. }
复制代码

省掉十几个样板行,只多了一个props getter。验证测试确认无误后,才放心大面积使用。其原理在基类重写operator==和hashCode,遍历props列表逐个比较,并做了runtimeType类型检查,确保不同子类实例不会相等。

继承场景:别忘了...super.props
项目中AdminUser继承User,多了一个role字段。关键就是用[...super.props, role]展开父类字段。一开始直接写props => [role],结果两个名字不同的AdminUser相等——父类的name、age等字段根本没参与比较。继承Equatable时务必用...super.props展开父类字段。

EquatableMixin:无法继承时使用
当类已经继承了其他类(如Flutter的State),可以用EquatableMixin混入。效果与继承Equatable完全一样,不影响继承链。可以封装一个EquatableState基类,让状态类继承之。

在Set和Map中当key用
值比较让对象可以放心放进Set或当Map的key。以前两个字段值相同的对象因引用不同在Set中占两个位置,用Equatable后按值去重。同样,Map以对象为key时,同值对象也能查到。这在缓存、去重、分组等场景特别实用。

空props的小技巧
props返回空列表时,所有实例都被视为相等。在Bloc模式中,只关心事件发生而不关心具体内容时很有用。

与鸿蒙ArkTS对比
ArkTS(TypeScript超集)中===也是引用比较,但生态没有现成的equatable库。做法有:自己写equals方法,每个类手动写(与Dart手工==一样烦);或用Lodash的isEqual做深比较,性能开销大且包体积大。对比之下,Dart的equatable声明式列出比较字段,精准轻量,无运行时反射,性能开销可忽略。

踩过的坑
mutable props导致脏数据:模型中包含List<String>字段并列入props,修改列表内容后hashCode变了,Set/Map查不到了。解决方法:尽量用不可变数据结构,或改用ID字段做比较。
继承漏了props:看似不报错,逻辑却不对。养成习惯:每写子类先写props,把...super.props先写好再加自己字段。
toString可读性不好:Equatable自动生成格式为ClassName(field1, field2, ...),字段多时挤在一起。开发阶段自己覆盖toString只输出关键字段。
props列表别放null:若字段类型是非空但给了null,会运行时异常。建议props返回类型写List<Object?>(带问号)。

注意事项
props只放用于判断相等的字段,如name、age等;不应放时间戳、自增ID、缓存数据这些每个实例不同的字段。性能不是问题,一万个对象十万次比较Equatable比手动写==慢不到5%。和Freezed的比较:Equatable运行时比较,简单直接;Freezed编译期代码生成,功能更全。简单模型用Equatable即可,复杂场景可混用。

在HarmonyOS上运行Flutter项目,安装equatable只需一行依赖:equatable: ^2.0.7,然后flutter pub get。没有额外配置,纯Dart依赖,在鸿蒙上一样可用。demo代码在lib/demos/equatable_demo.dart,覆盖手工==、Equatable、继承、Mixin、空props、Set/Map用法、注意事项等内容。验证环境:Flutter 3.x + HarmonyOS 4.x(DevEco Studio模拟器),Dart SDK 3.x。

用了Equatable后,每个数据模型平均省七八行代码,二十多个模型加起来省两百行样板代码。每次少写一次==就少一个潜在bug点。如果你用过Java的Lombok或Kotlin的data class,会觉得Equatable思路特别亲切。
回复

使用道具 举报

发表于 昨天 13:05 | 显示全部楼层

Re: HarmonyOS Flutter数据模型优化:用Equatable告别

感谢楼主的详细分享!在HarmonyOS上跑Flutter,数据模型一多确实容易在 `==` 上踩坑,Equatable 这种声明式写法省心很多。特别是继承时用 `...super.props` 展开父类字段这点,之前我也漏过,排查起来挺隐蔽的。props 里放 `List` 的提醒也很实用,避免 null 导致的运行时问题。关于可变 props 导致 hash 变化的问题,我后来习惯用 ID 字段 + `Equatable` 配合不可变集合(如 `dart:collection` 的 `UnmodifiableListView`)来规避。总体感觉 Equatable 轻量无侵入,对鸿蒙 Flutter 项目很友好,新手也容易上手。再次感谢分享实战经验!
回复 支持 反对

使用道具 举报

发表于 昨天 13:05 | 显示全部楼层

Re: HarmonyOS Flutter数据模型优化:用Equatable告别

楼主分享得很详细,Equatable在Flutter项目里确实是节省样板代码的利器,尤其是模型多的时候,少写一个 `==` 就少一个潜在bug,这个感触太真实了。 你提到空props在Bloc里只关心事件发生的场景,这点我还没试过,但听起来很巧妙——相当于把所有同类事件实例视为等价,确实能简化某些状态机判断。 另外,关于mutable props导致hashCode变化的坑,我原来也踩过,后来干脆把List字段改成不可变的 `IList` 或者直接用 `id` 字段比较,省心很多。你们项目里对这类可变字段一般怎么处理?
回复 支持 反对

使用道具 举报

发表于 昨天 13:05 | 显示全部楼层

Re: HarmonyOS Flutter数据模型优化:用Equatable告别

楼主的分享太实用了!Equatable 确实解决了 Dart 模型样板代码的痛点,特别是继承场景下 `...super.props` 的坑,我之前就踩过,排查了很久才发现是父类字段没参与比较。Props 踩坑那块也很有价值,尤其是 mutable props 导致 hash 变脏的问题,之前没意识到,多谢提醒。另外用空 props 让实例全相等的技巧在 Bloc 事件去重场景真巧妙,省了不少手动逻辑。楼主能把 HarmonyOS + Flutter 的实战经验写这么细致,对新手太友好了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-21 08:18 , Processed in 0.028932 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部