Flutter2 の List.firstWhere で The return type 'Null' isn't a XXX, as required by the closure's context になる問題

はじめに

ソフトウェアエンジニアの谷藤です。

今回は、タイトルに記載の問題について解説します。

問題の整理

以下のように List の firstWhere を使って特定の値を取り出す際に、値が見つからない場合の throw を防ぐために nullを返す orElse をセットすると、The return type 'Null' isn't a 'String', as required by the closure's context. というエラーが出てしまいます。

final List<String> fruits = ['apple', 'orange', 'banana', 'cherry'];

final grape = fruits.firstWhere((fruit) => fruit == 'grape', orElse: () => null); // The return type 'Null' isn't a 'String', as required by the closure's context.

原因

firstWhere のコードを見てみると、List の要素の型と orElse で返す値の型が一致している必要がありました。

E firstWhere(bool test(E element), {E orElse()?}) {
  for (E element in this) {
    if (test(element)) return element;
  }
  if (orElse != null) return orElse();
  throw IterableElementError.noElement();
}

解決方法

collection ライブラリの firstWhereOrNull を使うと素早く解決することができます。 pub.dev

上記のコードを collection を使って変更してみます。

import 'package:collection/collection.dart';

final List<String> fruits = ['apple', 'orange', 'banana', 'cherry'];

final grape = fruits.firstWhereOrNull((fruit) => fruit == 'grape');

エラーが出なくなりました。

firstWhereOrNull のコードを見てみると、値が見つからない場合は throw ではなく null を返していることがわかります。

T? firstWhereOrNull(bool Function(T element) test) {
  for (var element in this) {
    if (test(element)) return element;
  }
  return null;
}

さいごに

少しでも参考になったら嬉しいです。

質問やご指摘などがありましたら、コメントにお願いします。

読んでいただきありがとうございました。