MixinとStatic Extension Methodを使ってAutoDispose

Flutterアプリを作っていると、以下のようなコードをよく書くと思います。

class SampleWidgetSate extends State<SampleWidget> {
  AnimationController controller;
  
  ...
  
  @override
  void initState() {
    super.initState();
    controller = AnimationController(...);
  }
  
  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

AnimationControllerScrollControllerInputControllerなどはChangeNotifier を継承しているので、Widgetのdisposeのタイミングでdiposeを呼ぶ必要があります。

ただ、管理すべき~Controllerの数が増えると、 これらを複数行うのは面倒だしボイラープレートの冗長なコードが増えてしまいます。

そこで、MixinとDart2.7で追加されたStatic Extension Functionでこれを簡単に書けるようにしました。

以下のようなMixInとExtensionを書きます。

import 'package:flutter/widgets.dart';

mixin AutoDisposeStateMixin<T extends StatefulWidget> on State<T> {
  final _changeNotifiers = <ChangeNotifier>[];

  void addDisposer(ReactionDisposer dispose) {
    _disposers.add(dispose);
  }

  @override
  void dispose() {
    super.dispose();
    for (final notifier in _changeNotifiers) {
      notifier?.dispose();
    }
  }
}

extension AutoDisposeChangeNotifier on ChangeNotifier {
  void disposedBy(AutoDisposeStateMixin state) {
    state.addChangeNotifiers(this);
  }
}

これをインポートすると、以下のように書くことができるようになります。

class SampleWidgetSate extends State<SampleWidget> with AutoDisposeStateMixin {
  AnimationController controller;
  
  ...
  
  @override
  void initState() {
    super.initState();
    controller = AnimationController(...).disposedBy(this);
  }
}

結構短く書けるようになりました。Controllerが複数あるとさらに差がわかりやすいと思います。

ExtensionはDart2.7以降の機能なので、まだどのプロジェクトでも使えるわけではないですが、 Dartの可能性を大きく広げる機能だと思います。

ちなみに、今回のAutoDisposeRxSwiftのAPIに似せて作りました。

すでに拡張関数の利用例が豊富なKotlinやSwiftなどの先輩言語から 拡張関数の利用ケースを輸入するだけでも、コードをよりシンプルにできると思います。