FlutterでNNBDを有効化しても静的解析はできるけどコンパイルはできない件
FlutterプロジェクトでDartのNNBDを「有効化」しようとしたら静的解析はできるけどコンパイルはできなくて色々調べたので共有します。
NNBDとは
NNBD とは、 Non-Nullable By Default の略で、DartにおけるNull安全のための機能です。
通常なら int i; で宣言した i には null を代入することが可能ですが、NNBDを有効にすると null を代入することができなくなります。Null可型にするには int? i; で宣言する必要があります。
Dart単体でNNBDを体験してみる
まずはFlutter関係なくDart単体で体験してみましょう。
Flutterがインストールされている環境なら、
flutter channel devで チャンネル をdevに切り替えるとdartコマンドがグローバルで使えるようになるので、一時的に切り替えることで楽に最新版が導入できます。
1. 準備
適当にからのディレクトリを作って、その中に以下のようなファイルを作ります。
// test.dart
void main() {
int? i = null;
print('test');
}
int? i = null というNNBDを前提としたコードが含まれているのがポイントです。
2. 静的解析してみる
このファイルに対して dart analyze コマンドで静的解析を行ってみます。
$ dart analyze
Analyzing test... 0.9s
error • This requires the 'non-nullable' language feature to be enabled at test.dart:3:6 • (experiment_not_enabled)
info • The value of the local variable 'i' isn't used at test.dart:3:8 • (unused_local_variable)
2 issues found.
non-nullable 言語機能を有効にしないと使えない記法( int? )が使われてますよとエラーになりました。
3. analysis_options.yaml を設置して静的解析してみる
次に、同じディレクトリに以下の内容で analysis_options.yaml を作成します。
analyzer:
enable-experiment:
- non-nullable
この状態で再度 dart analyze してみると…
$ dart analyze
Analyzing test... 1.0s
info • The value of the local variable 'i' isn't used at test.dart:3:8 • (unused_local_variable)
1 issue found.
エラーが消えました。
4. コンパイルしてみる
では次に、 dart test.dart で実際にコードを(コンパイル&)実行してみます。
$ dart test.dart
test.dart:3:6: Error: This requires the 'non-nullable' language feature to be enabled.
Try updating your pubspec.yaml to set the minimum SDK constraint to 2.9 or higher, and running 'pub get'.
int? i = null;
^
静的解析ではエラーにならなかったのに、コンパイルはエラーになりました。
5. --enable-experiment=non-nullable オプションをつけてコンパイルしてみる
今度は --enable-experiment=non-nullable オプションをつけて実行してみましょう。
$ dart --enable-experiment=non-nullable test.dart
test
コンパイルが成功して正常に動きました。
FlutterプロジェクトでNNBDを有効化する方法?
ここまでで、Flutterから離れてDartの言語仕様としてのNNBDの使用方法について理解できたと思います。
では、FlutterプロジェクトでNNBDを有効化したい場合どうすればよいかを考えてみましょう。
ググってみると、
- プロジェクトルートに
analysis_options.yaml(内容は↑のとおり)を設置する -
pubspec.yamlのenvironment.sdkを>=2.6.0 <3.0.0(またはそれ以上)にする
といった情報がたくさん出てきます。
が、これは int? のような NNBD前提のコードをコンパイルできるようにするための方法ではない ので注意が必要です。
先に見てきたとおり、 analysis_options.yaml はあくまで静的解析のための設定ファイルであり、Dartを non-nullable 有効状態でコンパイルするにはコンパイル時にコマンドラインオプションを渡す必要があります。
念のため確認してみましょう。
静的解析してみる
コードの適当な箇所に int? i = null; という一文を入れてみます。
その上で flutter analyze を実行すると、
$ flutter analyze
Analyzing nnbd_sample...
error • This requires the 'non-nullable' experiment to be enabled • lib/main.dart:11:8 • experiment_not_enabled
info • The value of the local variable 'i' isn't used • lib/main.dart:11:10 • unused_local_variable
2 issues found. (ran in 6.4s)
見慣れたエラーが出力されました。
analysis_options.yaml を設置して静的解析してみる
続いて、プロジェクトルートに analysis_options.yaml を設置して(念のため flutter clean して)から再度実行してみます。
$ flutter analyze
Analyzing nnbd_sample...
info • The value of the local variable 'i' isn't used • lib/main.dart:11:10 • unused_local_variable
1 issues found. (ran in 1.2s)
エラーが消えました。
コンパイルしてみる
次に、この状態で flutter run で(コンパイル&)実行してみます。
$ flutter run
Launching lib/main.dart on iPhone SE (2nd generation) in debug mode...
Compiler message:
lib/main.dart:11:8: Error: This requires the 'non-nullable' experiment to be enabled.
Try enabling this experiment by adding it to the command line when compiling and running.
int? i = null;
^
Running Xcode build...
Xcode build done. 9.4s
Failed to build iOS app
Error output from Xcode build:
↳
** BUILD FAILED **
Xcode's output:
↳
Compiler message:
lib/main.dart:11:8: Error: This requires the 'non-nullable' experiment to be enabled.
Try enabling this experiment by adding it to the command line when compiling and running.
int? i = null;
^
Target kernel_snapshot failed: Exception: Errors during snapshot creation: null
build failed.
Command PhaseScriptExecution failed with a nonzero exit code
note: Using new build system
note: Building targets in parallel
note: Planning build
note: Constructing build description
Could not build the application for the simulator.
Error launching application on iPhone SE (2nd generation).
予想どおり、Dartの non-nullable が有効でないためコンパイルは失敗しました。
Flutterで non-nullable を有効にしてコンパイルすることはできるの?
ググてみると、いくつかこの問題についての質問が見つかります。
- This requires the 'non-nullable' experiment to be enabled. · Issue #51846 · flutter/flutter
- dart - Non-nullable by default: how to enable the experiment? - Stack Overflow
- Document how to run flutter commands with experiments · Issue #40490 · flutter/flutter
が、「静的解析を有効にすること」と「 non-nullable を有効にしてコンパイルすること」を混同している感じのレスが散見されたり、Dart単体で non-nullable を有効にする方法(本記事冒頭で説明したもの)にしか言及されていなかったりで、結局Flutterで non-nullable を有効にしてコンパイルすることは 現状はできない ようだというのが僕の結論です。(もし方法あるならぜひ 教えて いただけると嬉しいです🙇)
もし有効にできるとしても、NNBD前提で書かれていないサードパーティ製のライブラリとかがことごとくコンパイルエラーになっちゃうと思うので現状はまだ実質使える状況じゃないと考えてよいと思います。
analysis_options.yaml を設置して静的解析だけ有効にしておいて、 ? とかは使わずにコンパイル可能なコードのままできるだけNull安全なコードを心がける、というのが現状のFlutterでのNNBDの使い方ということでしょうか。
まとめ
-
analysis_options.yamlはただの静的解析の設定ファイルなので、これを設置したからといってNNBD前提のコードがコンパイルできるようになるわけではない - コンパイルできるようにするためにはDartの
non-nullableという機能を有効にする必要がある - Dart単体なら
dart --enable-experiment=non-nullable xxx.dartとオプションを渡すことでnon-nullableを有効にしてコンパイルすることができる -
flutter runの裏側で実行されるdartコマンドに--enable-experiment=non-nullableを渡す方法は(多分)現状は存在しない - 現時点で「FlutterでNNBDを有効にする」という言葉が意味するのは、単にNNBDとしての静的解析を有効にするという意味であり、NNBD前提のコードをコンパイルできるようにするという意味ではない
Discussion