はじめに
Flutterのアーキテクチャについての議論は様々で、infrastructure, dataSource, domainの単位でフォルダを大きく切り分けるlayered_architectureだったり、機能単位でフォルダを大きく分けるfeature_architecture
などが存在しています。
(以下の推奨を見ると、feature型の方が推奨されている感じですね)
その際に、あるあるなのは「共通実装どうしよう」という悩みです。
今回は、その共通化のための1手段であるMixinの使い方について記載します
Mixinとは?
Mixinは、新しい継承階層を作ることなく、クラスに機能を追加する方法です。つまり、元のクラスを変更することなく、特定のメソッドやプロパティをクラスに追加することができます。
基本的な定義の仕方
mixin
で定義するだけです。他のクラスを拡張してはならず、そのメソッドとプロパティはabstractとして宣言しなければなりません。
mixin SampleMixin {
void printMessage(String message) {
print(message);
}
}
使い方
使い方は以下のようにwith
を使います。
class Person with SampleMixin {
String name;
Person(this.name);
void introduce() {
printMessage("Hello, my name is $name.");
}
}
使い方の例
例えば、ログイン・ログアウトはAppBar
, 設定画面
, プロフィール画面
などで使い回すこともあるのではないでしょうか
mixin AuthMixin {
void login() {
// ログイン
}
void logout() {
// ログアウト
}
}
また、定型的なバリデーションもあれば使い回しも可能です。
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を使用することで、状態クラス内でアニメーションコントローラーを簡単に初期化・管理できます。
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を使用すると、ウィジェットが破棄されるのを防ぎ、状態を保持できるようになります。
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
メソッド内に実装することができます。これにより、アプリの状態に応じて適切な処理を実行することが可能になります。
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手として参考になればと思います。