🎄

GapというPackageを理解する

2023/12/13に公開

こんにちは!noseです。

この記事はスターフェスティバル Advent Calendar 2023の13日目の記事です。
https://qiita.com/advent-calendar/2023/stafes

はじめに

Flutterには様々なpackageが存在します。
今回は今更ながらGapについて理解を深めていこうと思い色々調べてまとめてみました。
https://pub.dev/packages/gap

現状の理解度

まずGapとはRowやColumnなどに対して自動で適切な方向に指定したマージンを入れてくれるWidgetを提供しているpackageである。というくらいの理解度を持っています。
具体的には

Column(
  children: [
    Text('項目1'),
    SizedBox(height: 8),
    Text('項目2'),
  ],
);

このようにColumn、Rowの子要素に対してマージンを開けたい場合は空のSizedBoxに高さもしくは幅を指定してあげることで実現しますがここにGapを使うとSizedBoxをGapに置き換えられます。

Column(
  children: [
    Text('項目1'),
    Gap(8),
    Text('項目2'),
  ],
);

コード数も少ないので影響があまりないように見えますが実際に使用する際は頻繁に使うことになるため、width/heightの指定が必要なくなり楽になるのと可読性が上がります。
また、偶にColumnなのにwidthで指定してしまってなんでだろう?ってなることも防げます。(自分だけかもしれない。。。。)

使い方

GapはいくつかのWidgetを提供しています。
基本的にはGapを使うと思いますが、それ以外にも以下のWidgetが提供されています。

SliverGap

SliverGapは文字通りsliverを使用する際に利用するWidgetです。

CustomScrollView(
  slivers: [
    SliverAppBar(),
    SliverGap(20),
    SliverToBoxAdapter(
      child: ....
    )
  ],
),

sliversには決められたWidget以外は使えないのでGapを使用するとエラーになります。
その代わりSliverGapを利用することで正常に使用できます。

MaxGap

MaxGapは余白の最大値を指定して0~最大値の幅で可能な範囲で最大の余白を表示してくれます。

このように青から黄色の余白は500を指定していますが、表示可能範囲は500に足りなくてもはみ出ない最大値で余白を設定してくれます。

中身を見る

では、実際にどのように実装をしているのかpackageの中身を見ていきます。
まずGapのbuildのなかを見てみると_RawGapを呼んでいます。
_RawGapはLeafRenderObjectWidgetを継承していてさらにその中でRenderGapというclassが生成されています。

class _RawGap extends LeafRenderObjectWidget {
  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderGap(
      mainAxisExtent: mainAxisExtent,
      crossAxisExtent: crossAxisExtent ?? 0,
      color: color,
      fallbackDirection: fallbackDirection,
    );
  }
}

RenderGapはRenderBoxを継承しています。
RenderGap内の_directionを見てみると親のNodeがRenderFlexであるかの判定を行っており、Column、Rowの場合ここで真の値になりそれぞれdirectionを返すことで縦の余白or横の余白の判定を行っていることがわかります。

Axis? get _direction {
  final parentNode = parent;
  if (parentNode is RenderFlex) {
    return parentNode.direction;
  } else {
    return fallbackDirection;
  }
}

ちなみにここで偽の値になるパターンとしてはListViewの子に直接入れるなどがありますが、その場合Gapの方で確認していた以下のfallbackDirectionが適用されるようです。
ただ、このような使われ方をすることがあまりなさそうな感じはします。

final scrollableState = Scrollable.maybeOf(context);
final AxisDirection? axisDirection = scrollableState?.axisDirection;
final Axis? fallbackDirection =
    axisDirection == null ? null : axisDirectionToAxis(axisDirection);

あとはこの_directionを描画時にチェックして縦or横にサイズを決定してwidgetが出来上がります。

最後に

ここ3年ほどFlutterで開発していますが、今回ネタに取り上げたGap以外にも様々なpackageにふれる機会がありました。
使っていて便利だなーと思いつつも中身までしっかり眺める機会もなかなかないのでこの機会に中身のコードまで調べてみるということをやってみましたが、そのpackageへの理解も深まるし、Flutterをより深く学べたと思います。
今回は割りと小さいpackageではありましたが、今後もいろんなpackageの中身を覗いていきたいと思います。

スタフェステックブログ

Discussion