3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Widgetの生成を条件分岐(if文)でコントロールする

Last updated at Posted at 2021-03-11

2021/3/16 : コメントの情報をもとに更新

#概要
FlutterのWidget制御で使えそうなTipsです。

Flutterを触り始めて2ヵ月程度ですが、
「ん?これどうなってんだ?」と少し詰まったところがありました。

同じように躓いた人の助けになれればいいなと思い投稿します。

#やりたいこと

Flutterでは、
すべてクラスやメソッドで画面側のコントロール(ウィジェット)を作成します。

「URLリンクのデータがあったら、URLリンク用のウィジェットを生成」
「データがないなら何もしない(ウィジェットのエリアは省略)」

こういうことがやりたかったんですが、ちょっとうまくいかなかった。

イメージ(脳内)

if(model.url != null)
true:URL用widget生成あり
false:URL用widiget生成なし

イメージ(コード)

こうあってほしい.dart
children: <Widget>[
  // urlリンクがあればリンク文字列用のwidgetを生成したい
  if(model.meetingUrl != null){
   MeetingUrlTextLabel(urlText: model.meetingUrl)
  }// elseはいらない  
]

#なぜうまくいかなかったか

先ほどの「こうあってほしい.dart」のままではエラーになります。
理由としては、if文はステートメント扱いのためのようです。

簡単に言うと、
ステートメントでは値を返すことも、返さないこともできるのでダメってこと。(と理解しました)

Children:内では、必ず1つのWidgetが返ってくる必要があります。
セミコロンが付いた文{};の{}内は自由に記述できますよね。

ステートメントについてコチラを参考にしました(私がC#畑出身のため)
https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/statements-expressions-operators/statements

対策としては、「即時関数を利用する」「三項演算子を使う」の2つがあります。

#即時関数を使う

即時関数.dart
children: <Widget>[
 (() {
   if (model.meetingUrl != null) {
     return MeetingUrlTextLabel(urlText: model.meetingUrl);
    }
   })(),
 ]

ちょっとカッコがとんでもないことになっちゃいます。
ここでやっているのは、「単一のウィジェットが返ってくる即時関数」を定義しています。

ちょっとわかりにくいですね。
三項演算子に置き換えると、かなり可読性が上がります。

#三項演算子を使う

三項演算子.dart
children: <Widget>[
 model.meetingUrl != null
  ? MeetingUrlTextLabel(urlText: model.meetingUrl)
  : SizedBox.shrink(),
]

割とシンプルにできました。

MeetingUrlTextLabelは自作のウィジェットクラスです。

三項演算子の場合、式なので値が必ず戻る必要があり、
真偽の両パターンで何か書かないといけません。

その場合は、SizedBox.shrink()で領域の無いウィジェットを返します。
Container等でも代用可能ですが、レイアウトに影響を与える可能性もゼロではないので
SizedBox.shrink()を使うのが一般的みたいです。

#[2021/3/16追記] collection ifを使う
コメントで教えていただいた情報で、
Dart 2.3 以降では if や for を使って collection内を組み立てられるようです。
https://dart.dev/guides/language/language-tour#collection-operators

Dartの静的解析のルールにもなっており、公式としてはcollection ifを推しているみたいです。

やっぱりDartの公式情報も見るのは大切ですね。
そら言語がDartなんだからそらそうだろって話なんですが、
Flutterの仕様を理解するので精一杯だったのです・・・。

というわけで、三項演算子ではなく、こちらを使うようにしましょう

CollectionIf.dart
children: [
  // constを書くとウィジェットがキャッシュされてパフォーマンス的に良いらしい
  if (model.meetingUrl == null) const SizedBox.shrink(),
  if (model.meetingUrl != null)
    MeetingUrlTextLabel(urlText: model.meetingUrl),
],

#まとめ

ウィジェットコードと処理コードが全く同じところに書くというのは
私にとって新しい文化でした。

本業ではC#のコーディング規約やアーキテクチャ支援をやっており、
責務ごとのアーキテクチャ分割は当たり前だったので、最初は可読性の低さに戸惑ったものです。

とはいえ、それなりにウィジェットをクラスやメソッドで分割したりできるので
案外可読性よく書けるのかなと。

また、ステートメント諸々について、
もし詳しい方いらっしゃったら教えてくれると嬉しいです!

3
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?