【Flutter】コンストラクタの書き方 (Dart Constructor)

この記事の情報は次のバージョンで動作確認しています。

  • MacOS Monterey (12.1)
  • Flutter (v2.10.4)
  • VSCode (v1.67.2)
  • Flutter

    はじめに

    Dartはコンストラクタの定義方法がありすぎです。

    なので備忘録も兼ねてまとめていこうと思います。

    前提

    NullSafetyを適用した場合の書き方になります。

    専門用語が出てくるのでおさらいしておきます。

    NonNullable

    Null非許容型 のことを指します。普通の変数のことです。
    例) int a; String b; など

    Nullable

    Null非許容型(オプショナルともいう)のことを指します。
    例) int? a; String? b;  ?をつけて変数宣言するやつ

    NonNullableかそうでないかで実装方法が全然変わりますので注意が必要です。

    基本パターン

    /* クラス側 */
    
    class TestClass1 {
      late int hoge1;
      late String hoge2;
    
      // コンストラクタ
      TestClass1(int param1, String param2) {
          hoge1 = param1;
          hoge2 = param2;
      }
    }
    
    // 呼び出し側
    var test = TestClass(1, "ABC");
    

    C#やJavaだとこういう書き方しますよね🤐

    呼び出し側は全ての引数に対して値をセットしないとエラーになります。

    クラス側はNonNullableな変数にはlateをつけないとエラーになります。
    NullSafetyを適用してる場合のみ

    this.で省略可

    変数に代入だけならthis.を使うともっと簡略して書くことができます。

    /* クラス側 */
    
    class TestClass2 {
      int hoge1;
      String hoge2;
    
      // コンストラクタ
      TestClass2(this.hoge1, this.hoge2);
    }
    
    /* 呼び出し側 */
    var test = TestClass(1, "ABC");
    

    これは他言語にはない仕様なので初見だと「コンストラクタなの?」って思いますよね😓

    しかもlateをつけなくてもいいみたいです。

    以降の引数指定の全てでこの方法が使えますので、覚えておきましょう。

    オプショナル引数 ( [引数1, 引数2] )

    実装方法

    [ ]の間に引数を定義します。

    /* クラス定義 */
    TestClass1(this.hoge1, [String? hoge, int param1 = 0]) {
        
    }
    
    /* 呼び出し側 */
    var test1 = new TestClass1(1); // オプショナル引数を省略
    var test2 = new TestClass1(2, "ABC"); // 1個だけ指定
    var test3 = new TestClass1(3, "DEF", 3); // 全部指定
    

    NonNullableな引数は初期値が必要

    引数がNonNullableだと初期値を入れないとエラーになります。

    // NG
    TestClass1([int param1]) { }  // NonNullableなのに初期値がないからエラー
    
    // OK
    TestClass1([int param1 = 0]) { }  // NonNullableだけど初期値があるからOK
    
    // OK
    TestClass1([int? param1]) { }  // Nullableだから初期値がなくてもOK
    

    ちなみにNGパターンだと以下のエラーメッセージが発生します。

    The parameter 'param1' can't have a value of 'null' because of its type, but the implicit default value is 'null'.
    Try adding either an explicit non-'null' default value or the 'required' modifier.

    まとめ

    • 基本パターンと併用して引数が定義可
    • 呼び出し側で引数を省略可
    • NonNullableな引数は初期値が必須

    ラベル付き引数 ( {引数1, 引数2} )

    実装方法

    { }の間に引数を定義します。

    /* クラス側 */
    TestClass1({int? param1, String param2 = "Hello"}) {}
    
    /* 呼び出し側 */
    
    // ① 「引数名: 値 」で指定していく
    var test1 = new TestClass1(param1: 0, param2: "ABC"); 
    
    // ② 引数順でなくてもいい 
    var test2 = new TestClass1(param2: "ABC", param1: 0); 
    
    // ③ 省略もできる
    var test3 = new TestClass1(param2: "ABC"); 
    

    NonNullableな引数は初期値が必須

    オプショナル引数と同様にNonNullableは初期値が必要です。

    class TestClass2 { // これはエラー TestClass2({int param}) {}

    // これならOK TestClass2({int param = 0}) {} }

    requiredをつけると省略不可にできる

    引数省略可がデフォルトですが、逆に一部の引数は必須にしたい場合もありますよね?

    そういうときはrequiredをつけます。

    requiredをつけるとNonNullableな引数も初期値が不要になります。

       // これはエラー
      TestClass1({int param1}) {  // NonNullableは初期値が必要
    
      }
      // これはOK
      TestClass1({required int param1}) {
    
      }
    

    以前は@をつけて@requiredという指定方法でしたが、現在は@なしになりました。

    古い記事とかだとその名残が残っているので注意が必要ですね。

    まとめ

    • 引数名:値の形式で引数をセットする
    • 引数指定の順序不問になった
    • 引数指定は省略可だがrequiredをつけることで必須にもできる
    • NonNullableな引数は初期値が必要
    • NonNullableな引数はrequiredをつけると初期値が不要