(2023.5.28更新)
Flutter/Dartの関数(メソッド)で、2つ以上の戻り値(返り値)を返したいけど、どうしたらいいの?
という方向けの記事です。
どうしても複数の戻り値を返したいときがあり、調べたところ、ニーズがないためか、当たり前過ぎるためか、、ググっても意外と日本語の情報が少なかったので、整理してみました。
調査・検討した結果、以下3つの方法があると理解しました。
- List型を戻り値にする
- Map型を戻り値にする
- Tuple型を戻り値にする(「tuble」パッケージが必要)
理解不足ゆえ、もっと良い方法があるようでしたら、是非ご指摘くださいm(_ _)m。
※クラス内の関数をメソッド(method)、クラス外の関数を関数(function)と呼びますが、本記事では「関数」に統一して記載しています。
40代からプログラミング(Flutter)を始めて、GooglePlayとAppStoreにアプリを公開しているhalzo appdevです。
作成したアプリはこちら↓ 全てFlutterで開発したアプリです。
Dartの標準文法にはない模様
Dartの公式サイト内を探ってみましたが、複数の戻り値を返す方法について、説明は見当たりませんでした。
「Functions」の説明
「Methods」の説明
こちらの記事にも、Dartで検討中との情報がありました。
もう少し調べると、GitHubにズバリのイシューが立っていました。
まだOpen中なので、2022.8時点でも検討中のようです。
プログラミング言語には、複数の戻り値を返せる言語、返せない言語があるようですが、今のところ、Dartは返せない言語みたいです。
【更新情報】
その後、前述のとおり、2023.5.11に、Flutter 3.10とともにリリースされたDart 3.0では、「Record」という型が導入され、これにより標準で複数の戻り値が返せるようになりました(上記イシューもクローズになっています)。
そのため、以降の説明は、Dart 3.0未満を利用される方向けの説明となります。
List型を戻り値にする
何か方法がないか考え、最初に思いついたのが、List型を戻り値にする方法です。
return ["文字列", 100, 0.55, true]
のように、戻り値を指定します。
複数の型を返したい場合は、関数の戻り値の型を、
- 書かない
- varにする
- List<dynamic>にする
のいずれかにすれば、問題なくできました。
ただ、戻り値を使用するとき、配列番号で指定する必要があるので、可読性にやや難がある印象です。
Map型を戻り値にする
次に考えたのは、Map型を戻り値にする方法です。
return {"戻り値1": "文字列", "戻り値2": 100, "戻り値3": 0.55, "戻り値4": true}
のように、戻り値を指定します。
こちらも複数の型を返したい場合は、関数の戻り値の型を、
- 書かない
- varにする
- Map<String, dynamic>にする
のいずれかにしておけば、問題なくできました。
Tuple型を戻り値にする
Tuple型について
こちらに紹介されていました。
Tuple型の定義は、言語によっても異なるようですが、大まかには複数の異なる型の組み合わせを、1つの変数で持つ方法で、List型やMap型と異なり、後で要素を直接変更できないものと理解しました。
Dartには標準でTuple型がないので、使用するには、下記の「tuple」パッケージを導入する必要があります。
ターミナルから、「flutter pub add tuple」でインポートします。
Tupleパッケージの使い方
例えば、4つの戻り値を返したい場合は、以下のように記述します。
return Tuple4<String, int, double, bool>("文字列", 100, 0.55, true);
「Tuple」の後に書く数字が、戻り値の数を表しています(直接数字を書くところがやや戸惑いましたが)。
ソースコードを調べると、「Tuple2」から「Tuple7」までのクラスが用意されているので、7つの値までなら定義できるようです。※「Tuple8」以上にしたらエラーになりました。
List型やMap型のケースと比べ、各要素の型を明示できるのはメリットかと思いました。
また、Tuple型の各要素にアクセスするには、「item1」から「item7」というプロパティが用意されているので、
Tuple型の変数名.item3
などとすることで、各要素を取り出すことができます(上記は3番目の要素にアクセスする例)。
Tuple型の注意点
前述のとおりTuple型は、要素を直接変更できません。
直接変更しようとすると、
'item2' can't be used as a setter because it's final.
のようなエラーになります。
但し、一度作成したTuple型の変数を、別のTuple型で再代入することは可能です。
サンプルコード
3つの戻り値を、List型、Map型、Tuple型で返す場合のサンプルコードを作成したので、ご参考に共有します。
なお、任意のモデルクラスも戻り値として返せることを確認するため、
- String(文字列)型
- ModelDataO(自作したモデルクラス)型
- double型
の3つを返すケースとして作成しました。
コピーして貼り付ければ、そのまま実行いただけます。
※コンソールに結果を表示することが目的なので、画面表示は、ダミーの空Containerにより真っ暗になります。
// クラス名、メソッド名、プロパティ名(変数名)について、筆者が作成したもの(名前変更可のもの) // の名前の末尾には、大文字のオー「O」をつけています // ※ライブラリ(パッケージ)で予め決められているもの(名前の変更不可のもの)と、 // 自分で作成したもの(名前の変更可のもの)の区別をしやすくするため import 'package:flutter/material.dart'; import 'package:tuple/tuple.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "Sample code", home: SampleScreen(), ); } } // bool型とint型の変数からなるモデルクラスを定義 class ModelDataO { final bool testBoolO; final int testIntO; const ModelDataO({ required this.testBoolO, required this.testIntO, }); } class SampleScreen extends StatefulWidget { @override _SampleScreenState createState() => _SampleScreenState(); } class _SampleScreenState extends State<SampleScreen> { // 3つの戻り値をList型で返す場合のメソッド定義 List<dynamic> testMethodByListO() { String textO = "Test text"; ModelDataO modelDataO = ModelDataO( testBoolO: true, testIntO: 105, ); double doubleValueO = 3.14; return [textO, modelDataO, doubleValueO]; } // 3つの戻り値をMap型で返す場合のメソッド定義 Map<String, dynamic> testMethodByMapO() { String textO = "Test text"; ModelDataO modelDataO = ModelDataO( testBoolO: true, testIntO: 105, ); double doubleValueO = 3.14; return {"String型": textO, "ModelDataO型": modelDataO, "double型": doubleValueO}; } // 3つの戻り値をTuple型で返す場合のメソッド定義 Tuple3<String, ModelDataO, double> testMethodByTupleO() { String textO = "Test text"; ModelDataO modelDataO = ModelDataO( testBoolO: true, testIntO: 105, ); double doubleValueO = 3.14; return Tuple3<String, ModelDataO, double>(textO, modelDataO, doubleValueO); } @override Widget build(BuildContext context) { // List型を返すメソッドの呼び出し List<dynamic> returnListO = testMethodByListO(); print("Listを使った結果"); // 1つ目の戻り値を表示 print(returnListO[0]); // 2つ目の戻り値を表示(モデルクラス内の変数を個別に表示) print(returnListO[1].testBoolO); print(returnListO[1].testIntO); // 3つ目の戻り値を表示 print(returnListO[2]); // Map型を返すメソッドの呼び出し Map<String, dynamic> returnMapO = testMethodByMapO(); print("Mapを使った結果"); // 1つ目の戻り値を表示 print(returnMapO["String型"]); // 2つ目の戻り値を表示(モデルクラス内の変数を個別に表示) print(returnMapO["ModelDataO型"].testBoolO); print(returnMapO["ModelDataO型"].testIntO); // 3つ目の戻り値を表示 print(returnMapO["double型"]); // Tuple型を返すメソッドの呼び出し Tuple3<String, ModelDataO, double> returnTupleO = testMethodByTupleO(); print("Tupleを使った結果"); // 1つ目の戻り値を表示 print(returnTupleO.item1); // 2つ目の戻り値を表示(モデルクラス内の変数を個別に表示) print(returnTupleO.item2.testBoolO); print(returnTupleO.item2.testIntO); // 3つ目の戻り値を表示 print(returnTupleO.item3); // 画面の描画は不要なため空Containerを設置 return Container(); } }
コンソールの実行結果は以下のとおりです。いずれの方法でも、同じ結果が得られています。
I/flutter ( 1751): Listを使った結果 I/flutter ( 1751): Test text I/flutter ( 1751): true I/flutter ( 1751): 105 I/flutter ( 1751): 3.14 I/flutter ( 1751): Mapを使った結果 I/flutter ( 1751): Test text I/flutter ( 1751): true I/flutter ( 1751): 105 I/flutter ( 1751): 3.14 I/flutter ( 1751): Tupleを使った結果 I/flutter ( 1751): Test text I/flutter ( 1751): true I/flutter ( 1751): 105 I/flutter ( 1751): 3.14
なお、戻り値用のモデルクラスを作り、それを戻り値にすることでも、同じことができると気づきました。。(記述量が多くなるので、かなり面倒ですが)
まとめ(どれがよいか?)
目的によって、以下のように使い分けるのが良いと感じました。
- 手軽に済ませたいとき → List型
- 可読性を高めたいとき → Map型
- 要素(個々の戻り値)の型を明示したいとき → Tuple型
以上、ご参考になれば幸いです。
最後までお読みいただき、ありがとうございました。
個人アプリ開発で役立ったもの
おすすめの学習教材
\超初心者向けでオススメな元Udemyの講座/
\キャンペーン時を狙えば安価で網羅的な内容が学べる(日本語訳あり)/
\Gitの基礎について無料で学べる/
おすすめの学習書籍
\実用的。image_pickerに関してかなり助けられた/
\Dartの基礎文法を素早くインプットできる/
コメント