Flutterでmixinを使う

## はじめに
Flutterのアーキテクチャについての議論は様々で、infrastructure, dataSource, domainの単位でフォルダを大きく切り分ける[layered_architecture](https://verygood.ventures/blog/very-good-flutter-architecture)だったり、機能単位でフォルダを大きく分ける`feature_architecture`などが存在しています。

(以下の推奨を見ると、feature型の方が推奨されている感じですね）

https://codewithandrea.com/articles/flutter-project-structure/

https://github.com/bizz84/flutter-tips-and-tricks/blob/main/tips/0039-flutter-project-structure-feature-first-or-layer-first/index.md

その際に、あるあるなのは「共通実装どうしよう」という悩みです。

今回は、その共通化のための1手段である[Mixin](https://dart.dev/language/mixins)の使い方について記載します

## Mixinとは？
Mixinは、新しい継承階層を作ることなく、クラスに機能を追加する方法です。つまり、元のクラスを変更することなく、特定のメソッドやプロパティをクラスに追加することができます。

## 基本的な定義の仕方
`mixin`で定義するだけです。他のクラスを拡張してはならず、そのメソッドとプロパティはabstractとして宣言しなければなりません。

```dart
mixin SampleMixin {
  void printMessage(String message) {
    print(message);
  }
}
```

## 使い方
使い方は以下のように`with`を使います。
```dart
class Person with SampleMixin {
  String name;

  Person(this.name);

  void introduce() {
    printMessage("Hello, my name is $name.");
  }
}
```

## 使い方の例
例えば、ログイン・ログアウトは`AppBar`, `設定画面`, `プロフィール画面`などで使い回すこともあるのではないでしょうか
```dart
mixin AuthMixin {
  void login() {
    // ログイン
  }

  void logout() {
    // ログアウト
  }
}
```

また、定型的なバリデーションもあれば使い回しも可能です。
```dart
mixin ValidationMixin {
  bool emptyCheck(String text) {
    return text.isNotEmpty;
  }

  bool validateEmail(String email) {
    RegExp regex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
    return regex.hasMatch(email);
  }
}
```

以上のようにすることで`utility`などの使用を避けたコードの再利用が可能となります。
また、Flutterではデフォルトでウィジェットに対する`mixin`がいくつか存在しています。

### SingleTickerProviderStateMixin:
このmixinは、アニメーションを扱う際に役立ちます。SingleTickerProviderStateMixinは、アニメーションコントローラーにTicker（フレームごとの時間差を提供するオブジェクト）を提供するために使用されます。このmixinを使用することで、状態クラス内でアニメーションコントローラーを簡単に初期化・管理できます。
```dart
class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this, // 使用するTickerProviderを指定
      duration: const Duration(seconds: 2),
    );
  }

  // 必要な箇所でアニメーションを実装
}
```

### AutomaticKeepAliveClientMixin:
このmixinは、ウィジェットの状態を保持するのに役立ちます。一部のウィジェットは、親ウィジェットが再構築される際に状態を保持したい場合があります。これを実現するために、AutomaticKeepAliveClientMixinを使用できます。このmixinを使用すると、ウィジェットが破棄されるのを防ぎ、状態を保持できるようになります。
```dart
class _MyStatefulWidgetState extends State<MyStatefulWidget> with AutomaticKeepAliveClientMixin {
  // 状態を保持したいデータやウィジェットの実装

  @override
  bool get wantKeepAlive => true; // 状態の保持を有効化

  @override
  Widget build(BuildContext context) {
    super.build(context); // 必ずsuper.build(context)を呼び出すこと
    // ウィジェットの構築
  }
}
```

### WidgetsBindingObserver:
このmixinは、アプリのライフサイクルイベントを監視するために使用されます。WidgetsBindingObserverを使用すると、アプリがアクティブ化・非アクティブ化されたときや、画面の向きが変わったときなどのイベントに対応できます

以下の例では、`WidgetsBindingObserver`を使用して、アプリのライフサイクルイベントを監視しています。それぞれのイベントが発生したときの処理を`didChangeAppLifecycleState`メソッド内に実装することができます。これにより、アプリの状態に応じて適切な処理を実行することが可能になります。


```dart
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this); // オブザーバーの登録
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this); // オブザーバーの解除
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    switch (state) {
        case AppLifecycleState.resumed:
            // アプリがアクティブ化されたときの処理
            break;
        case AppLifecycleState.inactive:
            // アプリが非アクティブ化されたときの処理
            break;
        case AppLifecycleState.paused:
            // アプリが一時停止されたときの処理
            break;
        case AppLifecycleState.detached:
            // アプリがウィジェットツリーから削除されたときの処理
            break;
    }
  }

  @override
  Widget build(BuildContext context) {
    // ウィジェットの構築
  }
}
```

以上となります。`utility`を作ってしまうと簡単かもしれませんが、そうすると`utility`が肥大化してしまう場合がありますね。そのため、今回の`mixin`はそれを避ける1手として参考になればと思います。