【Flutter】その強制アンラップ、モテませんよ？

かれこれ4年以上、楽しいFlutterライフを送っているokeです！

Flutterにて使用されているDart言語は年々進化しており、とてもワクワクしますよね。

その進化の一環として導入されたのが「[Sound Null Safety](https://dart.dev/null-safety)」。
これにより通称 “ヌルポ”(Null Pointer Exception)からはおさらばした…はずだったのですが、
ふとした拍子に **Unexpected null value** なんてエラー、経験ありませんか？

このエラー、実は”**非モテコード**”が原因かも？

今回は、”**モテコード**”に進化するためのポイントを、
ある日のokeと恋愛マスターが解説します(?)


### 要約（忙しい人のために）
下記のようなnullチェックを行っているコードがあった時に、
```dart
class TestWidget extends StatelessWidget {
  const TestWidget({required this.child1, required this.child2});

  final Widget? child1;
  final Widget? child2;

  @override
  Widget build(BuildContext context) {
    if (child1 != null && child2 != null) { // nullチェック！
      return Row(
        children: [child1!, child2!], // 強制アンラップ！
      );
    }
```
下記のようにif文の条件をうっかり削除してしまうこともあるので、
```dart
// child2を消してしまった！でもエラーは出ない！
if (child1 != null) {
  return Row(
    children: [child1!, child2!],
  );
}
```
下記のように強制アンラップはなるべく使用せず書く方が良いよねという物語です…！
```dart
  @override
  Widget build(BuildContext context) {
    final child1 = this.child1;
    final child2 = this.child2;
    if (child1 != null && child2 != null) {
      return Row(
        children: [child1, child2],
      );
    }
```

### プロローグ 〜ある日のこと〜

oke「最近、プログラムも恋もうまくいかないなぁ…」

恋愛マスター：「やあ、oke。なにか悩みごとかい？」

oke：「そうなんです。実は最近好きな子ができたんですけど、デートの誘いがどうもうまくいかなくて…」

恋マス「なるほど。それは**Dartコードを見直すと解決する**かもな」

oke「え!? 本当ですか!?」

恋マス「あぁ、本当だ。早速"恋"と"Dartコード"を見ていこうか」

oke「はい！よろしくお願いします！」

### シーン1：強引な恋愛はNG

恋マス「時に、君はSound Null Safetyを知っているか？」

oke「もちろんです、Dartコードの基本ですよね。ヌルポがなくなってさいこぅ〜」

恋マス「じゃあ、**nullableな値があったときのこと**を考えてみようか。例えば、こんな感じでokeくんのパートナー名とその日のムードを取り扱うクラスがあったとする」

```dart
// パートナーの情報を管理するクラス
class Partner {
  Partner({required this.name, this.mood});

  /// パートナーの名前
  final String name;

  /// その日のムード（気分）
  final String? mood;
}
```

恋マス「そして、デートに誘う関数もある」

```dart
/// デートに誘う関数
void proceedWithDate({
    required String name,
    required String mood
})
```

恋マス「これらを使って、デートに誘ってみなさい」

oke「あぁそれだけか。楽勝ですねっ！こんな感じですか？」

```dart
void goGoGo({required Partner partner}) {
    proceedWithDate(
        name: partner.name,
        mood: partner.mood!, // 強制アンラップ
    ); 
}
```

恋マス「なるほどね。君は**強引なタイプ**のようだ」

oke「...と、いいますと？」

恋マス「そのコードは恋愛でいうと、”**相手がOKしていると思って強引にアプローチしてしまうようなもの**”だよ。君が”最高のムードだ”と思っても、相手にとっては”**こんなのムードが台無しっ！絶対なし(null)だわ！**”と思われてるかもしれない」

oke「な、なるほど…。確かにmoodがnullになったらデートのお誘いは成功しませんね…」

恋マス「そうだ。**多分大丈夫だろう**と大した根拠もなしに強制アンラップをするのは**ご法度**なんだ」

oke「うぅ…自分を過信していました…」

### シーン2：強制アンラップの罠

oke「じゃ、じゃあこのコードなら良いってことですね！」

```dart
void checkDateAvailability({required Partner partner}) {
    if (partner.mood == null) {
        // mood（気分）が分からなければ次に進まない
        return;
    }
  
    proceedWithDate(
        name: partner.name,
        mood: partner.mood!, // 強制アンラップ
    ); 
}
```

恋マス「そうだな。そのコードは日によっては成功するだろう」

oke「おっ、やった！！じゃあ早速あの子に会いに行ってきま…」

恋マス「だが、**100点ではない**」

oke「えっっ」

恋マス「では追加のパターンを考えてみようか。デートに誘えるかはその日のムードだけではない。事前に**相手の空いている日**を把握しておくことや**お気に入りの場所**などを聞き出しておくことも必要だ」

oke「た、たしかに…」

恋マス「では、その2つを追加したクラスがこれだ」

```dart
class Partner {
  Partner({
    required this.name, 
    this.mood, 
    this.availableDate,
    this.favoritePlace,
  });

  /// パートナーの名前
  final String name;
  
  /// その日のムード（気分）
  final String? mood;
  
  /// 空いている日
  final DateTime? availableDate;

  /// お気に入りの場所
  final String? favoritePlace;
}
```

恋マス「これを使ってデートに誘ってみなさい」

oke「わ、わかりました…！こんな感じですかね？」

```dart
void checkDateAvailability({required Partner partner}) {
    if (partner.favoritePlace == null || partner.availableDate == null) {
        // 空いている日やお気に入りの場所がわからないならデートの誘いをやめる
        return;
    }
  
    // デートに誘う
    proceedWithDate(
        name: partner.name,
        mood: partner.mood!,
        favoritePlace: partner.favoritePlace!,
        availableDate: partner.availableDate!,
    ); 
}

```

oke「よ、よし！一応動作確認しましたが、問題なさそうです！ではあの子に会いに行ってきま…」

恋マス「okeくん、君は気付いたかい？」

oke「な、なんですか…？」

恋マス「**ムードのチェックを忘れていることに**」

oke「…ほ、ほんとだぁー！！コードを書き換える時にうっかり消してしまいました…」

恋マス「プログラムも恋も、ステップを抜かしたら上手くいかない。さっきは**動作確認のときにmoodがnullのパターンで確認しなかった**だろう？」

oke「うぅ…忘れていました…」

恋マス「一見うまくいっていたように見えても、実は**恋にもコードにも落とし穴はある**んだ」

oke「なるほど…勉強になります…」

### シーン3：“モテる”秘訣

oke「じゃ、じゃあ恋愛マスターはどのようにステップを進めるのですか!?」

恋マス「そうだな。恋愛でもコードでも、”**しっかり確認しながら進める**”が基本だ。これが正しいエスコートだ」

```dart
void checkDateAvailability({required Partner partner}) {
    // 1つずつnullチェックすることで、
    // 強制アンラップを使用しなくて良くなる。
    final mood = partner.mood;
    final favoritePlace = partner.favoritePlace;
    final availableDate = partner.availableDate;
    if (mood == null
        || favoritePlace == null
        || availableDate == null) {
        return;
    }
  
    proceedWithDate(
        name: partner.name,
        mood: mood,
        favoritePlace: favoritePlace,
        availableDate: availableDate,
    ); 
}
```

oke「なるほど…。強制アンラップで"こうでしょっ！"と決める必要もなくなって、これなら間違ってデートに誘う心配はない！」

恋マス「そうだ。**男には強引さも必要だが、それは時として失敗を招くこともある**。よく肝に銘じておきなさい」

oke「わかりました！**では、あの子に会いに行ってきますっ！**」

スタタタっ…

恋マス「…あぁ行ってしまったか。**一見遠回りに見える丁寧なnullチェックが実は最短距離**であり、コードも恋も急がば回れということを伝えたかったんだが…。猪突猛進で向かう姿はたくましいが、まずはLINEで連絡をせねばなぁ…」

### まとめ

架空の恋愛マスターを登場させて強制アンラップの重要なポイントを解説しました！

「そうだ、強制アンラップについての記事を書こう！」と思いついて書き始めたら、いつの間にかこんな記事になってしまいました…。

「わかりにくい！」とか「ここの例え間違ってる！」などあれば遠慮なくご指摘ください！
それでは良い"**モテFlutterライフ**"を！

### 補足

今回紹介したように、Flutterで強制アンラップを乱用することは、リスクがあると思っています。

はじめの例で出した「nullになる可能性のある箇所をアンラップ」するのは禁物ですが、
2つ目の例の「nullチェックを直前にしてその後にアンラップする」というコードをよく見かけたので今回の記事を書くことにしました。
※コードが書き換わりnullチェックがなくなった際、アンラップだけが残りエラーが出てしまう可能性があると思っています。

また、「nullになる想定ではない箇所」についても「なるべくnullチェックを設けてエラーをユーザーに伝えてあげる仕組み」は設けておくべきかなと思っています。
（もちろんエラーを監視ツールにログを送ることも忘れずに！）

ただ、自分も考慮できていないパターンもあると思っていまして、
強制アンラップを逆に「使用した方が良い箇所」があれば、理由とともに教えていただければ嬉しいです…！