トラブルシュート#7-3: Null check operator used on a null valueエラーを理解し、解決する
Flutter開発を進めていると、ある日突然、赤いエラー画面と共に以下のようなメッセージに遭遇することがあります。
The following _CastError was thrown...: Null check operator used on a null value
または
Unhandled Exception: Null check operator used on a null value
このエラーは、FlutterのNull Safety(ヌル安全)という強力な機能に関連しています。一見すると不親切なエラーに見えますが、実は「アプリがクラッシュする可能性のある、重大なバグを未然に防いでくれた」という、ありがたい警告なのです。
このレシピでは、このエラーの根本原因と、それを解決するための考え方を学びます。
1. Null Safetyとは? - 「nullは許可しない」という原則
DartのNull Safetyは、非常にシンプルな原則に基づいています。
「デフォルトでは、すべての変数はnull(何もない状態)になることを許可しない。」
例えば、以下のコードはエラーになります。
// エラー! String型の変数nameは、nullになることを許可されていない
String name;
print(name);
これにより、変数が初期化し忘れられてnullのまま使われ、アプリが予期せずクラッシュする、という古くからあるバグの温床を根絶しています。
もし、変数がnullになる可能性を意図的に許可したい場合は、型名の後ろに?を付けます。
// OK。String?型の変数nameは、Stringまたはnullの値を持つことができる
String? name;
print(name); // nullと表示される
2. エラーの根本原因:!(バン演算子)による「無謀な断定」
Null check operator used on a null valueエラーが発生する原因は、ほぼ100%、!(バン演算子、またはNull-check operator)という演算子にあります。
!演算子は、プログラマがコンパイラ(Dartの検査官)に対して行う、「強い断定」です。
nullableString! というコードは、
「おい、コンパイラ!このnullableString変数は、型の上ではnullになる可能性がある(String?型)とされているが、このコードが実行されるこの瞬間においては、絶対にnullではないことを私が保証する!だから、安心してString型として扱ってくれ!」
という、非常に強い意思表示です。
エラーは、この「約束」が破られた時に発生します。
あなたが「絶対にnullじゃない!」と断定したにもかかわらず、実際にはその変数がnullだった場合、Dartの実行環境は「約束が違うじゃないか!危険なので処理を中断します!」と言って、このエラーを発生させるのです。
コード例
String? nullableName; // nullになりうるString型の変数。現在の値はnull。
// どこかの処理で、名前が代入される...はずだったが、されなかったとする
// プログラマは「この時点では、もう名前は入っているはずだ」と信じて、! を使ってしまう
int length = nullableName!.length; // ★ここでエラーが発生!
// 「nullableNameはnullじゃないと断定したのに、実際はnullでしたよ!」
// → Null check operator used on a null value
3. 解決策:!を避け、安全な方法でnullを扱う
このエラーを解決する最善の方法は、安易に!演算子を使わないことです。代わりに、nullの可能性を安全に処理するための、Dartが提供する便利な方法を使いましょう。
解決策A: if文によるNullチェック(最も確実)
最も基本的で、確実な方法です。
String? nullableName;
if (nullableName != null) {
// このブロックの中では、nullableNameは絶対にnullではないとコンパイラが理解してくれる
// そのため、`!`を付けなくても、String型として安全に扱える
int length = nullableName.length;
print('名前の長さは $length です。');
} else {
// nullだった場合の処理
print('名前が設定されていません。');
}
解決策B: ?.(Null-aware access operator) - もしnullなら、何もしない
プロパティにアクセスする際に、?.を使うと、「もしオブジェクトがnullでなければ、そのプロパティにアクセスして。もしnullなら、式全体の結果をnullにしてね」という意味になります。
String? nullableName;
// nullableNameがnullなので、.lengthにはアクセスせず、式全体の結果がnullになる
int? length = nullableName?.length;
print(length); // null と表示される
解決策C: ??(If-null operator) - もしnullなら、デフォルト値を使う
??演算子は、「左辺がnullでなければ左辺の値を、もしnullなら右辺のデフォルト値を使ってね」という意味です。
String? nullableName;
// nullableNameがnullなので、右辺の 'ゲスト' が使われる
String displayName = nullableName ?? 'ゲスト';
print('ようこそ, $displayName さん!'); // ようこそ, ゲスト さん! と表示される
まとめ
Null check operator used on a null valueエラーは、敵ではなく、あなたのコードの安全性を高めてくれる「味方」です。
このエラーに遭遇したら、「ああ、!で無謀な断定をしてしまったな」と考え、if文や?.、??といった安全な方法で、nullの場合の処理をきちんと記述するように心がけましょう。安易な!の使用は、将来のバグの種を蒔いていると覚えておくことが重要です。