⚗️

Dartのextensionを使ってみる

2023/10/13に公開

Dartのextensionの使い方

公式のリンク

公式より引用

拡張メソッドは、既存のライブラリに機能を追加します。 知らず知らずのうちに拡張メソッドを使用している可能性があります。 たとえば、IDE でコード補完を使用すると、通常のメソッドとともに拡張メソッドが提案されます。

そもそもextensionとは、既存のクラスに対してメソッドを追加する機能です。

使い方は以下の通りです。

extension 名前 on{
  // 拡張メソッド
  void メソッド名() {
    // 処理
  }
}

BuildContextに拡張メソッドを追加する例を示します。

スナックバーとかはよく使うので、関数化してみる。

import 'package:flutter/material.dart';

extension ContextEx on BuildContext {
  // スナックバーを表示する
  void showSnackBar(String message) {
    // 引数にthisを指定することで、このメソッドを呼び出したコンテキストを取得できる
    ScaffoldMessenger.of(this).showSnackBar(SnackBar(
      content: Text(message),
    ));
  }

  // ダイアログを表示する
  // ジェネリティクスにTを指定することで、戻り値の型を指定できる
  Future<T?> showAlertDialog<T>({
    required String title,
    required String content,
    required List<Widget> actions,
  }) async {
    return showDialog<T>(// showDialogメソッドはFuture<T>を返す
      context: this,// 引数にthisを指定することで、このメソッドを呼び出したコンテキストを取得できる
      builder: (context) => AlertDialog(
        title: Text(title),
        content: Text(content),
        actions: actions,
      ),
    );
  }
}

時間を扱うDateTimeクラスに拡張メソッドを追加する例を示します。

毎回時間を書くコードを書くのは冗長なので関数にしてみた。

// 時間を扱う拡張メソッドを定義するファイル
extension DateTo on DateTime {
  // 年月日を表示する
  String toFormatString() {
    // thisは省略できる
    return "$year$month$day日";
  }

  // 曜日を表示する
  String toWeekdayString() {
    // 書いてある数字は、DateTimeクラスの定数
    switch (weekday) {
      case 1:
        return "月";
      case 2:
        return "火";
      case 3:
        return "水";
      case 4:
        return "木";
      case 5:
        return "金";
      case 6:
        return "土";
      case 7:
        return "日";
      default:
        return "";
    }
  }
}

このように拡張メソッドを定義することで、以下のように呼び出すことができます。

import 'package:flutter/material.dart';
import 'package:widget_cook/extension/date_extension.dart';
import 'package:widget_cook/extension/extension.dart';

class ExtensionCook extends StatelessWidget {
  const ExtensionCook({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // 時間のの拡張メソッドを使う
        title: Text(DateTime.now().toFormatString()),
      ),
      body: Center(
        child: Column(
          children: [
            // 今日の曜日を表示する
            Text(DateTime.now().toWeekdayString()),
            const SizedBox(height: 10),
            ElevatedButton(
                onPressed: () {
                  // 使うときは、context.メソッド名()で呼び出せる
                  context.showSnackBar("スナックバー");
                },
                child: const Text("スナックバー")),
            const SizedBox(height: 10),
            ElevatedButton(
                onPressed: () {
                  context.showAlertDialog(
                    title: "タイトル",
                    content: "コンテンツ",
                    actions: [
                      TextButton(
                        onPressed: () {
                          Navigator.pop(context);
                        },
                        child: const Text("キャンセル"),
                      ),
                      TextButton(
                        onPressed: () {
                          Navigator.pop(context);
                        },
                        child: const Text("OK"),
                      ),
                    ],
                  );
                },
                child: const Text("ダイアログ"))
          ],
        ),
      ),
    );
  }
}

最後に

拡張メソッドを使うことで、コードをスッキリさせることができます。複数のファイルで使う場合は、extensionフォルダを作成して、そこにまとめると良いでしょう。これで再利用性の高いコードを書くことができます。

Swiftみたいに、Dartでもクラスに対して拡張メソッドが使えるみたいです。メソッドを追加しただけですが、何か参考になるかも?
enumで型を定義して、typedefでオリジナルの型を作ってモデルで使ってます。

// bool型のメソッドで使うEnum
enum TaskStatus {
  done,
  notDone,
}

// やること終わったかのオリジナルのデータ型を作る
typedef IsDone = TaskStatus;

// やることの状態を保持するモデル
class Task {
  final String title;
  final IsDone isDone;

  Task({required this.title, required this.isDone});
}

// Swiftみたいにクラスに、extensionでロジックの追加ができる!
extension TaskExtension on Task {
  // bool型のメソッドを定義する
  bool isTaskCompleted() {
    return isDone == TaskStatus.done;
  }
}

void main() {
  // モデルにデータを入れる
  var shopping = Task(title: '買い物は終わりました', isDone: TaskStatus.done);
  // extensionで追加したisTaskCompletedを使ってtrue || falseか判定する
  print(shopping.isTaskCompleted()); // true
  // モデルにデータを入れる
  var working = Task(title: 'お仕事は終わってません', isDone: TaskStatus.notDone);
  // extensionで追加したisTaskCompletedを使ってtrue || falseか判定する
  print(working.isTaskCompleted());// false
}

Discussion