👏

先取り Dart 3.7 の変更点

2024/12/21に公開

https://github.com/dart-lang/sdk/blob/main/CHANGELOG.md#370

結構ありそうだったのでまとめます。

執筆当時の最新はまだ Dart 3.6 なので、是非皆さんも 3.7 アプデ時に「ついにこの機能来るのかぁ」とドヤ顔できるように頑張りましょう。

Wildcard

https://github.com/dart-lang/language/blob/main/accepted/future-releases/wildcard-variables/feature-specification.md

使用しないコールバックの引数などに 慣例的に 命名していた _ 変数に、ワイルドカードとしてのちゃんとした機能を付与する。

👇 こういうやつ

showDailog(
  builder: (_) => MyDialog(),
);

特に、Dart 3.0 で導入された Record でもたまに使用するようになった。

final myRecord = (1, 'a', false));

final (_, str, __) = myRecord;

print(str);

付与される機能

  • _ 変数の使用を禁止する
    今までは、慣例的に _ をつけていただけなので、_ 変数を使って print したりできましたが、それができないようになりました。

    慣例が、ちゃんとした機能になりました。👏(たまに print でログに出してたのができなくなりました)

  • _ 変数の複数宣言を可能にする
    今まで、使わない変数が複数ある場合は _____ と、アンスコを増やさないといけませんでしたが、全て _ で対応できるようになりました。

    // before
    PageRouteBuilder(
      pageBuilder: (_, __, ___) {
        return Container();
      }
    )
    
    // after
    PageRouteBuilder(
      pageBuilder: (_, _, _) {
        return Container();
      }
    )
    

    DartPad で試した結果

Analyzer

VScode, Android Studio とかの Quick Fix の補完機能が、結構充実するそうです。

Expanded/Flexibleの補完

今まで手入力していたExpanded/Flexibleが補完できるようになりました。

Before

After


さらにポイントとして、Expanded/FlexibleColumnRow の children として使う前提のウィジェット(ParentDataWidget<FlexParentData>)であるので、ColumnRow の children 出なければ、サジェストされません(すごい)。

相対パス・絶対パスのサジェスト

Assists and quick fixes that add an import now consider the prefer_relative_imports and always_use_package_imports lint rules.

たとえば、相対パスのみで運用する警告を有効にした時に効力を発揮します。

linter:
  rules:
    prefer_relative_imports: true
import '../../service/shared_preferences/base_shared_preferences_service.dart';
import '../../service/shared_preferences/shared_preferences_key.dart';

import 'package:my_app/service/shared_preferences/shared_preferences.dart';
// Use relative imports for files in the 'lib' directory.
// Try converting the URI to a relative URI.dartprefer_relative_imports

Before

相対パスのみなのに、package: import がサジェストされる。

After

相対パスのみサジェストされる。

await で解決できるとこの補完

Add a fix that wraps an expression in await if the expression is currently not assignable, but awaiting it would make it assignable.

たとえば、以下のように StringFuture<String> を受け取るとき、もちろん型が違うのでエラーが発生します。

Future<String> futureString() {
  return Future.value('xxx');
}

void main() async {
  var str = '';
  // A value of type 'Future<String>' can't be assigned to a variable of type 'String'.
  str = futureString();
  
  print(str);
}

Before

単に「型が違う」とだけ解釈される。

After

await の解決策も提示して quick fix ができる。
たまに発生してたのでありがたい。

cascade_invocations の quick fix

Add an additional fix to correct a cascade_invocations lint rule violation.

ref.invalidate(userProvider);
ref.invalidate(postProvider);
ref.invalidate(notificationProvider);

↓ quick fix

ref..invalidate(userProvider)
..invalidate(postProvider)
..invalidate(notificationProvider);

Before

2つを1つにすることはできていた。
なので、3つ以上の場合は1ずつつ行う必要があった。

After

3つ以上の場合も一気にできるようになった。

forEach を for-loop にするとき

prefer_final_in_for_each と always_specify_types を考慮してアシスト(Before みた方がわかりやすい)。

Before

var ではなく final」「型の宣言」の警告が出るのがわかってるのに、その形になってしまう。

After

1発で、警告を考慮した変換をしてくれる。

else 内の if-else を、else if に変換

Offer an assist to "inline" an else-block's inner if-statement with the else-block to read else if. (Thanks @FMorschel for the above enhancements!

例えば、こういう関数があったとする。

  void wasmJudge() {
    if (!kIsWeb) {
      print('not Web');
    } else {
      if (kIsWasm) {
        print('is Wasm');
      }
    }
  }

この分岐は、else のなかの if を、else if に変換しても、論理的には同じになる。

  void wasmJudge() {
    if (!kIsWeb) {
      print('not Web');
    } else if (kIsWasm) {
      print('is Wasm');
    }
  }

After

これを quick fix で解決できる。
すごい。

else のなかの分岐が複数あっても対応できる。

🤔 ~/ 演算子

Add a new fix that converts a ~/ operation into /, when the ~/ operation is not available.

~/ が使えない時、/ へ変換する quick fix が出るっぽい。

試しに、double~/ の結果を入れる(~/ の結果は int なのでエラーになる)のやってみたけど、何も出ない。
これはなんだ。

その他、追加・削除ルール

  • Add the experimental specify_nonobvious_property_types lint rule.
  • Add the experimental omit_obvious_property_types lint rule.
  • Remove the package_api_docs lint rule.
  • Remove the unsafe_html lint rule.

Dart Format

コード1行の文字数が、今まで80文字をオンオフにするしかなかったのですが、好みに合わせて行数が指定できるようになりました。

analysis_options.yaml
include: package:flutter_lints/flutter.yaml

formatter:
  page_width: 123

一時的にフォーマットをオフにできるようになった

// dart format off から // dart format on までの範囲指定するっぽいです。

こーーれだいぶ嬉しいです。

main() {
  this.isFormatted();
  // dart format off
  no   +   formatting
    +
      here;
  // dart format on
  formatting.isBackOnHere();
}

たとえば、同じような要素を並べる時に、一部項目だけ表示が違うと少し見通しが悪くなったりします。

final hours = duration.inHours;
final minutes = duration.inMinutes.remainder(60);
final seconds = duration.inSeconds.remainder(60);
final milliSeconds = 
  duration.inMilliseconds.remainder(1000);
ProviderScope(
  overrides: [
      sharedPreferencesProvider
          .overrideWithValue(sharedPreferencesInfrastructure),
      inAppPurchaseProvider.overrideWithValue(inAppPurchase),
      googleAdmobProvider.overrideWithValue(googleAdmob),
  ],
);
``````dart
// dart format off
final hours = duration.inHours;
final minutes = duration.inMinutes.remainder(60);
final seconds = duration.inSeconds.remainder(60);
final milliSeconds = duration.inMilliseconds.remainder(1000);
// dart format on

フォーマッタのせいで逆に読みづらくなることはたまにあるので、うまく使っていきたいです。

ProviderScope(
  overrides: [
      // dart format off
      sharedPreferencesProvider
          .overrideWithValue(sharedPreferencesInfrastructure),
      inAppPurchaseProvider.overrideWithValue(inAppPurchase),
      googleAdmobProvider.overrideWithValue(googleAdmob),
      // dart format on
  ],
);

Discussion