👨‍💻

# Flutter3(Dart3)で、Swiftの値付きEnumを実現したい

2024/01/20に公開

Flutter3(Dart3)で、Swiftの値付きEnumを実現したい

Swiftのenumの値付きenum 「正式名称 associated Value」を、Flutterでも使いたく、Flutter3からの「sealed class」を使えば、同じ表現(※)を、実現できるのでその実装MEMOです。

※...switch文に対応し、個別に値を持たせられること

// 値付きenumの例
// これと同じ表現(※)を、Flutterで実現していきます。

// タップされた種別のenum定義
enum HogeScreenAction {
   // 閉じるボタン
   case CloseButton
   // 下部ボタン
   case ButtomButton(hogeCount: Int)
   // 送信ボタン   
   case PostButton(text: String,  id: Int)
}

Flutter3(Dart3)からの機能を使えば、Swiftの値付きenumを実装できるので、そのコードを実装します。
Swiftのenumに比べると、冗長にはなりますが、Swiftと同じ実装(switch分に対応、別々の型で値を自由に設定できる)が実現できたので、使っています。
(今後、Swiftなみに、手軽に定義できればよいのですが)

Flutterでの、Swiftの値付きenum の実現

注意点
  • 1, コード量が多くなる
  • 2, グローバルなスコープになる
1については、Swiftのenumと同じ表現を実装する場合、enumではなくclassを使うため、
その分定義するコードが多くなります。
ただ、一度定義してしまえば、利用側のコードでは、大差ないため、
利用できるケースが多いかと思います。

2については、Swiftだと、クラス内にenum定義することができますが、
Flutterではクラスをつかうため、スコープがグローバルになり、
その分、enum名が長くなります。

必要な対応

enumと各caseの定義(sealedクラス利用)

  • 1 sealedクラスで、共通クラスとして、定義する(Swiftのenum名)
  • 2 sealedクラスを継承して、各enumのケースを定義(Swiftの各case名)
    • 値を持たせたい場合、変数を定義して持たせる
/// - 1 sealedクラスで、共通クラスとして、定義する(Swiftのenum名)
/// sealed classで、「Swiftのenum名」として定義
sealed class HogeScreenAction {
  const ButtonTapAction._();
}

/// - 2 sealedクラスを継承して、各enumのケースを定義(Swiftの各case名)
// 閉じるボタン
class HogeScreenAction_CloseButton implements HogeScreenAction {
  const HogeScreenAction_CloseButton();
}

// 下部ボタン
class HogeScreenAction_BottomButton implements HogeScreenAction {
  // - 値を持たせたい場合、変数を定義して持たせる
  const HogeScreenAction_BottomButton({required this.hogeCount});
  final int hogeCount;
}

// 送信ボタン   
class HogeScreenAction_PostButton implements HogeScreenAction { 
  // - 値を持たせたい場合、変数を定義して持たせる
  const HogeScreenAction_PostButton({required this.text, required this.id});
  final String text;
  final int id;
}

Switch文での利用例

UI側で、State(状態変数)を更新やロジックを書いていたが、それを、UIから切り離してAction内に集約するよう修正した際のコード例。

  • switch文で値を取り出して利用

  // UI側からは、.sendAction( HogeScreenAction_CloseButton() ); とコールして実装。
  sendAction({required HogeScreenAction action}) async {
    // MEMO: 発火したアクション名をPrint()で確認
    print("[Action]---");
    print(action.runtimeType);
    print("-----------");

    switch (action) {
      case HogeScreenAction_CloseButton(): // 閉じるボタン
      // TODO: stateを操作
      break;
            
      case HogeScreenAction_BottomButton(hogeCount: final hogeCount): // 下部ボタン
      // TODO: stateを操作
      break;
      
      case HogeScreenAction_PostButton(text: final text, id: final id): // 送信ボタン
      // TODO: stateを操作      
      break;            
    }    
  }  
  

Discussion