flutter_blocでBlocパターンを実装する Equatable編

# Equatable
`Equatable` とは、オブジェクト比較を簡素化するためのパッケージです。
Dartでは、オブジェクトの値の比較に `==` オペレーターを使用しますが、カスタムクラスの場合、デフォルトでは参照の比較しか行われません。つまり、オブジェクトの値が同じでも、インスタンスが異なれば `==` は `false` を返してしまいます。

```dart: カスタムクラスオブジェクト比較 
class Person {
  final String name;
  final int age;

  Person(this.name, this.age);
}

void main() {
  var person1 = Person('John', 30);
  var person2 = Person('John', 30);
  var person3 = person1; // 同じインスタンスを参照

  print(person1 == person2); // false
  print(person1 == person3); // true

  print(person1.hashCode); // HashCodeは異なる
  print(person2.hashCode); // HashCodeは異なる
  print(person3.hashCode); // person1と同じHashCode
}
```

この問題を解決する際に、使用するのが `Equatable` です。
 `Equatable` を使うと、カスタムした複雑なオブジェクトでも、
オブジェクトのフィールド値を比較できるようになります。

### インストール
`Equatable` をFlutterで使用する際には `equatable` パッケージをインストールする必要があります。
以下のリンクからパッケージをインストールしましょう。

https://pub.dev/packages/equatable

```yaml
dependencies:
  equatable: ^2.0.0
```

## Equatableの使い方

`Equatable` を使用するには、対象のクラスで `Equatable` を継承し、 `props` メソッドを
オーバーライドする必要があります。

```dart
import 'package:equatable/equatable.dart';

class Person extends Equatable {
  final String name;
  final int age;

  Person(this.name, this.age);

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

void main() {
  final person1 = Person('John', 30);
  final person2 = Person('John', 30);
  final person3 = Person('Jane', 25);

  print(person1 == person2); // true
  print(person1 == person3); // false
}
```

### 1. Equatableの基本
まず、 `import 'package:equatable/equatable.dart';` を使用して 
`Equatable` パッケージをインポートします。 クラスで `Equatable` を継承し、
`props` メソッドをオーバーライドします。

```dart
import 'package:equatable/equatable.dart';
class Person extends Equatable {
  final String name;
  final int age;

  Person(this.name, this.age);

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

### 2. propsメソッドのオーバーライド
`props` メソッドは、等価性の比較を行うためのプロパティのリストを
返却するために使用します。このリストを定義することで、オブジェクトが持つ値をもとに
等価性を判断することができます。

例えば、この例では、 `name` と `age` の2つのプロパティがリストに含まれています。
これらのプロパティが同じであればオブジェクトは等価とみなされます。

```dart
@override
List<Object?> get props => [name, age];
```

### 3. 等価性の比較
`==` 演算子を使用して、2つの `Person` オブジェクトの比較を行います。
この例では、 `person1` と `person2` は同じ `name` と `age` を持っているため、`true` が返されます。
一方、 `person1` と `person3` は異なるプロパティを持っているため、 `false` が返されます。

```dart
void main() {
  final person1 = Person('John', 30);
  final person2 = Person('John', 30);
  final person3 = Person('Jane', 25);

  print(person1 == person2); // true
  print(person1 == person3); // false
}
```

## BLoCでのEquatableの使用
BLoCパターンでは、StateクラスやEventクラスに `Equatable` を使用することで、
状態の変化を効率的に検出することができます。

```dart: イベント(Event)クラスの例
import 'package:equatable/equatable.dart';

abstract class CounterEvent extends Equatable {
  const CounterEvent();

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

class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}
```

イベントクラスは、ユーザーの操作やアプリの動作により発生するアクションを定義します。
ここで定義したアクションは、 `Bloc` に対して何らかの処理を行うよう指示します。

- **抽象クラス (CounterEvent):**

全てのイベントクラスの親クラスとして機能しています。

- **イベントサブクラス (Increment・Decrement):**

`CounterEvent` クラスを継承し、それぞれカウンターの値を増加、
減少させるイベントを表します。

```dart: 状態(State)クラスの例
import 'package:equatable/equatable.dart';

class CounterState extends Equatable {
  final int count;

  const CounterState(this.count);

  @override
  List<Object?> get props => [count];
}
```
状態クラスでは、アプリケーションの現在の状態を定義します。
`Bloc` はこの状態を管理し、イベントに応じて状態を更新します。

- **状態クラス (CounterState):**
アプリケーションの具体的な状態を表しています。
この例では、カウンターの値を保持しており、 `props` メソッドで、
等価性の比較に使用するプロパティ（`count`）を返却しています。

### BLoCクラスの例
```dart
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState(0)) {
    on<Increment>((event, emit) => emit(CounterState(state.count + 1)));
    
    on<Decrement>((event, emit) => emit(CounterState(state.count - 1)));
  }
}
```

このコードは、 `Bloc` パターンを使用してカウンターアプリのビジネスロジックを実装しています。
`Bloc<CounterEvent, CounterState>` を継承しており、
`CounterEvent`はイベントタイプ、 `CounterState` を状態タイプとして指定しています。
`CounterBloc` クラスは、イベントに応じてカウンターの状態を管理するBlocです。

- **`on<Increment>`:** `Increment` イベントが発生した場合の処理を定義します。
`event` は受け取ったイベント、 `emit` は新しい状態を発行するための関数です。
ここでは、現在のカウンターの値に1を加えた新しい状態を発行しています。

- **`on<Decrement>`:** `Decrement` イベントが発生した場合の処理を定義します。
ここでは、現在のカウンターの値から1を引いた新しい状態を発行しています。

# まとめ
このように、 `Equatable` を使うことで、BLoCパターンでの状態管理が効率的になります。
オブジェクトの等価性を簡単に定義できるため、ステートやイベントの比較が容易になり、
パフォーマンスの向上が期待できます。
これを機にぜひ `Equatable` を導入してみてください。

# 告知
最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを
募集しております。詳しくは採用情報ページをご確認ください。

https://eda-inc.jp/recruit/

みなさまからのご応募をお待ちしております。