【Flutter】DartでResult型を使う
要約
result_dart は、成功と失敗を明示的に返す「Result」をDartで使えるようにするパッケージです。これにより、try/catch の散在や「どこで例外が出るのか分かりにくい」問題を軽減し、UI・状態管理層で明示的にハンドリングできます。Kotlin、SwiftのResultに着想を得ており、Dart3のパターンマッチングとも相性が良いです。
対象
- FlutterやDartを使い始めた初心者
- try/catch文で例外を処理するのではなく、メソッド単位で明示的に失敗処理をする方向に切り替えたい人
問題設定
Dartの例外は宣言的ではありません。関数が例外を投げるかどうかは式からは分からず、呼び出し側に try/catch を書き忘れると未処理例外→クラッシュを招きます。結果として、データ層からリポジトリ、ViewModelやControllerの各レイヤーで例外の伝播が不透明になります。公式ドキュメントも、こうした課題への対策として Resultパターンの有用性を解説しています。
解決アプローチ
基本的な使い方
戻り値をSuccess()、Failure()で囲ってreturnします。
Result<String> getSomethingPretty() {
if(isOk) {
return Success('OK!');
} else {
return Failure(Exception('Not Ok!'));
}
}
戻り値は拡張メソッドを使うこともできます。
ただ、この拡張メソッドはFuture型やResult型には使えないため、その点だけ注意してください。
Result<String> getSomethingPretty() {
final isOk = Random().nextBool();
if(isOk) {
return 'OK!'.toSuccess();
} else {
return Exception('Not Ok!').toFailure();
}
}
値の取得方法は以下のように、パターンで取得できます。
Future<void> loadUser(int id) async {
final result = await _repo.fetchUser(id);
switch (result) {
case Success(value: final data):
print('User loaded: ${data['name']}');
case Failure(exception: final e):
print('Failed: $e');
}
}
取得方法は他にもあり、fold()を使うと、Failureの際には加工した値を取得したいということもできます。
void main() {
final result = getSomethingPretty();
final String message = result.fold(
(success) {
// handle the success here
return "success";
},
(failure) {
// handle the failure here
return "failure";
},
);
}
値の取得する際のutilityメソッドがいくつか用意されているので、気になる方はresult_dartのドキュメントを確認してみてください。
まとめ
例外を「値」に変えることで、失敗を型で表現し、明示的に実装することができます。toSuccess()やtoFailure()の拡張メソッドはStringやExceptionのような具体的な値に対して呼び出し、その値をResult型に変換するために使用します。Result、Future型には使用できないのは注意して使用してください。
Discussion