Dartのコンストラクタでインスタンスメソッドを上書いてインスタンス変数を変更したかった(未解決)

タイトル通りなのですが、結論から言うと、現時点ではできないと思います。

きっかけはありがちなこんなコード。

class AwesomeButton extends RaisedButton {
  int _index = 1;
  AwesomeButton()
      : super(
            child: const Text('Awesome!'),
            onPressed: () {
              print(_index);
            });
}

タップする度にインスタンス変数を増やすボタンを作りたいな、と思ったものの、とりあえず通ることを確認しようとprintしただけでコンパイルエラー。

Only static members can be accessed in initializers.

staticにすればビルドは通りますが、インスタンス変数で持ちたい。
調べたところ、次のようなIssueがあがっていました。

Allow read-only access to initializing formals. - GitHub

この問題みんな困っていたみたいで各所からリファレンスされているのですが、タイトル通りread-onlyでコンストラクタ内でインスタンス変数へのアクセスを許可したよ、とのこと。
具体的には次のような変更だったようです。

  • The grammar is unchanged.
  • An initializing formal this.x of a constructor C introduces the name x into a scope that covers C's initializers, but not the body; it is considered to be a final parameter.
  • The semantics of such an initializing formal access is that it accesses the parameter, not the field. This matters because the initializing formal may be captured by a function expression, and the field may be updated.
  • 文法は変更なし。
  • this.xの形でコンストラクタに渡せるようにはしたけど、コンストラクタのボディでは渡せないよ。
    (渡す値はfinalのパラメータを渡す想定だよ)
  • こういうinitializeする時にアクセスするのってfieldじゃなくてparameterなはずだよね。
    functionを渡してfieldを上書けるようにしちゃう問題を起こすかもだからこうしてるよ。
    ※ 超意訳かつふんわり理解なので、間違っていたらご指摘ください。

つまり、こういうことはできるようになりました。

class AwesomeButton extends RaisedButton {
  int _index = 1;
  // 引数にthis.xの形で値を渡せる
  AwesomeButton(this._index)
      : super(
            child: Text('Awesome!'),
            onPressed: () {
              print(_index);
            });
}
class C1 {
  final int x;
  // 単独でもセットできるし
  C1(this.x);
}

class C2 {
  final int x;
  final int y;
  // コロンの後でも代入できるし
  C2(this.x) : y = x + 1;
}

class C3 {
  int x;
  // ボディでも渡せる
  C3(this.x) { x = 100; }
}

class C41 {
  int y;
  int z () {
    return y;
  }
  C41(this.y);
}

class C42 extends C41{
  int x;
  int y;
  // スーパークラスのコンストラクタにも渡せる
  C42(this.x, this.y) : super(y){
    x = 100;
  }
}

けれども、スーパークラスのコンストラクタにファンクションを渡すとエラーになる

class C51 {
  int y;
  int z() {
    return y;
  }

  C51(this.y, this.z);
}

class C52 extends C51 {
  int x;
  int y;
  C52(this.x, this.y)
      : super(y, () { print('hello'); });
}

// => 'z' isn't a field in the enclosing class.エラー

RaisedButtonのサブクラスみたいにプロパティとして渡す場合は上書けるけど、「setterが無い」と怒られる。

class AwesomeButton extends RaisedButton {
  int _index;

  AwesomeButton(this._index)
      : super(
            child: Text('Awesome!'),
            onPressed: () { _index = 10; });
}
// => Setter not found: '_index'.

setterを作ってもだめ。

class AwesomeButton extends RaisedButton {
  int _index;
  void get index => _index;
  void set index(suuji) => _index = suuji;
  AwesomeButton(this._index)
      : super(
            child: Text('Awesome!'),
            onPressed: () { index(10); });
}

// => Only static members can be accessed in initializers.dart(implicit_this_reference_in_initializer)
// => The expression here has a type of 'void', and therefore cannot be used.
// (※ setterはvoidである必要がある)

というわけで調査した結果、ほとほとつかれはてて「Flutterはコンストラクタでインスタンス変数を上書きするようインスタンスメソッドを定義できない」という結論になったのでした。
冒頭で引用した通りの思想なのだと思いますが、ほんとうにつかれました。。。。。