🐷

スクロール可能/不可能状態でLazyVerticalGridの挙動を変える

2023/08/09に公開

プロジェクトで以下のようなCard Composableコンポーネントを作成する要件があったので、その時の知見を共有します。

黒が全体のSizeで、
橙がCard、
青がLazyVerticalGrid(LazyColumn)、
ピンクがLazyVerticalGrid(LazyColumn)内のアイテム、
緑がボタンとします。

想定挙動では、LazyVerticalGrid(LazyColumn)内のアイテムが少ない時は、↓

LazyVerticalGrid(LazyColumn)内のアイテムが多い時は、↓

のように動作して欲しいとします。要するに、高さ可変のBottom Sheetの中に個数可変のリストが入っているコンポーネントです。
この時、modifierに何も指定しないとアイテムが多い時に↓

という挙動になってしまいます。これを回避するために、LazyVerticalGrid(LazyColumn)のmodifierにModifier.weight(1f)を追記すると、今度はアイテムが少ない時に↓

という挙動になってしまいます。
この要件を整理すると、LazyVerticalGrid(LazyColumn)がスクロール可能でない限りはCard ComposableをwrapContentにして、スクロール可能==全画面表示になればボタンを表示するためにweightを設定する必要がある、と言えます。
そこで、スクロール可能/不可能状態を定義しModifierの挙動を分岐させることにしました。
まず、stateを用意します。(LazyColumnの場合はrememberLazyListState())

val state = rememberLazyGridState()

次に、stateにある関数canScrollForwardcanScrollBackwardを活用します。
developerによると、

canScrollBackwardは

Whether this ScrollableState can scroll backward (consume a negative delta). This is typically false if the scroll position is equal to its minimum value, and true otherwise.
Note that true here does not imply that delta will be consumed - the ScrollableState may decide not to handle the incoming delta (such as if it is already being scrolled separately). Additionally, for backwards compatibility with previous versions of ScrollableState this value defaults to true.

canScrollForwardは

Whether this ScrollableState can scroll forward (consume a positive delta). This is typically false if the scroll position is equal to its maximum value, and true otherwise.
Note that true here does not imply that delta will be consumed - the ScrollableState may decide not to handle the incoming delta (such as if it is already being scrolled separately). Additionally, for backwards compatibility with previous versions of ScrollableState this value defaults to true.

とのことなので、(state.canScrollForward || state.canScrollBackward)である時、このLazyVerticalGrid(LazyColumn)はスクロール可能である、と言えます。
modifierを以下のように変更します。

LazyVerticalGrid(
    modifier = if (
        state.canScrollForward
        || state.canScrollBackward
    ) {
        Modifier.weight(1f)
    } else {
        Modifier
    },
    state = state
) {}

これによって、想定動作になりました。

株式会社THIRD エンジニアブログ

Discussion