💻

FlutterでNNBDを有効化しても静的解析はできるけどコンパイルはできない件

2020/07/17に公開

FlutterプロジェクトでDartのNNBDを「有効化」しようとしたら静的解析はできるけどコンパイルはできなくて色々調べたので共有します。

NNBDとは

NNBD とは、 Non-Nullable By Default の略で、DartにおけるNull安全のための機能です。

通常なら int i; で宣言した i には null を代入することが可能ですが、NNBDを有効にすると null を代入することができなくなります。Null可型にするには int? i; で宣言する必要があります。

参考:DartのNull安全導入状況 (NNBD) - Qiita

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.yamlenvironment.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 を有効にしてコンパイルすることはできるの?

ググてみると、いくつかこの問題についての質問が見つかります。

が、「静的解析を有効にすること」と「 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前提のコードをコンパイルできるようにするという意味ではない
GitHubで編集を提案

Discussion