搜索插件、代码仓与 APP 分类

插件详情v3.0.0

equatable

一个 Dart 包,可帮助实现基于值的相等性,而无需显式重写 == 和 hashCode。

AndroidiOSWebmacOSWindowsLinux
近 30 天下载
736万
喜欢数
3640
Pub 分数
160

安装插件

flutter pub add equatable

插件介绍

基于插件公开信息、社区数据与项目使用情况整理的选型参考。

Equatable 是一个 Dart 包,通过根据对象的属性自动生 == 和 hashCode 方法来简化基于值的相等性实现。它消除了手动重写这些方法的需要,减少了样板代码并降低了错误风险。适用于模型、状态以及任何相等性取决于字段值而非身份的类。该包与 Flutter 及其他 Dart 项目无缝集成,提升了代码的可读性和可维护性。

适合场景

  • 在状态管理中比较模型对象
  • 简化 Redux 或 Bloc 模式中的相等性检查
  • 减少数据类中的样板代码
  • 验证响应式 UI 中的状态变化
  • 确保集合中的一致哈希

主要特点

  • 自动 == 和 hashCode 生成
  • 最小化样板代码
  • 对嵌套和复杂类型的支持
  • 与 Flutter 状态管理的轻松集成
  • 通过混入扩展

适合对象

  • 使用 Bloc 或 Redux 的开发者
  • 注重代码清晰性和可维护性的团队
  • 需要频繁进行数据比较的项目
  • Dart 开发人员避免手动相等逻辑
  • 需要可预测状态更新的应用程序

使用建议

  • 可能会略微增加构建时间
  • 不适用于基于引用的相等性
  • 需要谨慎处理空值
  • 对复杂相等规则的自定义有限
  • 最好与不可变数据结构一起使用

原始 README

以下为关联源码仓库的 README 原文快照,最新内容请访问源码仓库。

logo

Simplify Equality Comparisons

Build Status Code Coverage Pub Package
Star on GitHub Discord MIT License


Overview

Being able to compare objects in Dart often involves having to override the == operator as well as hashCode.

Not only is it verbose and tedious, but failure to do so can lead to inefficient code which does not behave as we expect.

By default, == returns true if two objects are the same instance.

Let's say we have the following class:

class Person {
  const Person(this.name);

  final String name;
}

We can create instances of Person like so:

void main() {
  final Person bob = Person("Bob");
}

Later if we try to compare two instances of Person either in our production code or in our tests we will run into a problem.

print(bob == Person("Bob")); // false

For more information about this, you can check out the official Dart Documentation.

In order to be able to compare two instances of Person we need to change our class to override == and hashCode like so:

class Person {
  const Person(this.name);

  final String name;

  @override
  bool operator ==(Object other) =>
    identical(this, other) ||
    other is Person &&
    runtimeType == other.runtimeType &&
    name == other.name;

  @override
  int get hashCode => name.hashCode;
}

Now if we run the following code again:

print(bob == Person("Bob")); // true

it will be able to compare different instances of Person.

You can see how this can quickly become a hassle when dealing with complex classes. This is where Equatable comes in!

What does Equatable do?

Equatable overrides == and hashCode for you so you don't have to waste your time writing lots of boilerplate code.

There are other packages that will actually generate the boilerplate for you; however, you still have to run the code generation step which is not ideal.

With Equatable there is no code generation needed and we can focus more on writing amazing applications and less on mundane tasks.

Usage

First, we need to do add equatable to the dependencies of the pubspec.yaml

dependencies:
  equatable: ^3.0.0

Next, we need to install it:

# Dart
dart pub get

# Flutter
flutter packages get

Lastly, we need to extend Equatable

import 'package:equatable/equatable.dart';

class Person extends Equatable {
  const Person(this.name);

  final String name;

  @override
  List<Object> get props => [name];
}

When working with json:

import 'package:equatable/equatable.dart';

class Person extends Equatable {
  const Person(this.name);

  final String name;

  @override
  List<Object> get props => [name];

  factory Person.fromJson(Map<String, dynamic> json) {
    return Person(json['name']);
  }
}

We can now compare instances of Person just like before without the pain of having to write all of that boilerplate. Note: Equatable is designed to only work with immutable objects so all member variables must be final (This is not just a feature of Equatable - overriding a hashCode with a mutable value can break hash-based collections).

Equatable also supports const constructors:

import 'package:equatable/equatable.dart';

class Person extends Equatable {
  const Person(this.name);

  final String name;

  @override
  List<Object> get props => [name];
}

Equatable also supports nullable props:

import 'package:equatable/equatable.dart';

class Person extends Equatable {
  const Person(this.name, [this.age]);

  final String name;
  final int? age;

  @override
  List<Object?> get props => [name, age];
}
toString Implementation

Equatable can implement toString method including all the given props. If you want that behaviour for a specific Equatable object, just include the following:

@override
bool get stringify => true;

For instance:

import 'package:equatable/equatable.dart';

class Person extends Equatable {
  const Person(this.name);

  final String name;

  @override
  List<Object> get props => [name];

  @override
  bool get stringify => true;
}

For the name Bob, the output will be:

Person(Bob)

This flag by default is false and toString will fallback to the default implementation:

Instance of Person

EquatableConfig

stringify can also be configured globally for all Equatable instances via EquatableConfig

EquatableConfig.stringify = true;

If stringify is overridden for a specific Equatable class, then the value of EquatableConfig.stringify is ignored. In other words, the local configuration always takes precedence over the global configuration.

Note: EquatableConfig.stringify defaults to true in debug mode and false in release mode.

Recap

Without Equatable
class Person {
  const Person(this.name);

  final String name;

  @override
  bool operator ==(Object other) =>
    identical(this, other) ||
    other is Person &&
    runtimeType == other.runtimeType &&
    name == other.name;

  @override
  int get hashCode => name.hashCode;
}
With Equatable
import 'package:equatable/equatable.dart';

class Person extends Equatable {
  const Person(this.name);

  final String name;

  @override
  List<Object> get props => [name];
}

Mixin Usage

Sometimes it isn't possible to extend Equatable because your class already has a superclass. In this case, you can still get the benefits of Equatable by using Equatable as a mixin.

Usage

Let's say we want to make an EquatableDateTime class, we can use Equatable like so:

class EquatableDateTime extends DateTime with Equatable {
  EquatableDateTime(
    int year, [
    int month = 1,
    int day = 1,
    int hour = 0,
    int minute = 0,
    int second = 0,
    int millisecond = 0,
    int microsecond = 0,
  ]) : super(year, month, day, hour, minute, second, millisecond, microsecond);

  @override
  List<Object> get props {
    return [year, month, day, hour, minute, second, millisecond, microsecond];
  }
}

Now if we want to create a subclass of EquatableDateTime, we can just override props.

class EquatableDateTimeSubclass extends EquatableDateTime {
  final int century;

  EquatableDateTimeSubclass(
    this.century,
    int year,[
    int month = 1,
    int day = 1,
    int hour = 0,
    int minute = 0,
    int second = 0,
    int millisecond = 0,
    int microsecond = 0,
  ]) : super(year, month, day, hour, minute, second, millisecond, microsecond);

  @override
  List<Object> get props => [...super.props, century];
}

Benchmarks

You can see and run performance benchmarks by heading over to benchmarks.

Maintainers

所属分类

依赖

依赖清单 2 项

源码仓库

使用此插件的项目

所在榜单