【Flet】GridViewの内容を中央揃えで表示したい【Python】

# やりたいこと
Fletには複数のコントロールをいい感じに一覧表示してくれるGridViewコントロールが存在します。

https://flet.dev/docs/controls/gridview/

このGridViewがどのような働きをするのかは、公式のアイコンブラウザーを見ればだいたい分かると思います。

https://gallery.flet.dev/icons-browser/

このGridView、表示するコントロールの数が少ないときには左上にすべてが寄るかたちになって少し寂しい見た目になってしまいます。（GridViewの使用場面を考えれば当然ですが）

![](https://storage.googleapis.com/zenn-user-upload/41196ed763cf-20241130.png)

これを以下のように中央に寄せて表示したい場面があったのですが、一筋縄ではいかなかったのでその方法を記しておきます。

![](https://storage.googleapis.com/zenn-user-upload/eeee12fadf4f-20241130.png)

# 結論
GridViewをContainerの中に入れ、Containerの内容を中央揃えにするようにします。
そして、GridViewの幅（`width`プロパティ）をその`max_extent`プロパティと`controls`プロパティ（これはリスト型）の長さを掛けた値に設定すれば解決です。

**ソースコード**
```python
import flet as ft

def main(page: ft.page):
    gv = ft.GridView(
        spacing=30,
        run_spacing=30,
        max_extent=200,
    )

    ct = ft.Container(
        content=gv,     # GridViewを内包
        alignment=ft.alignment.center,  # Containerの内部を中央寄せ
        expand=1,       # expandを1に設定することでGridViewがスクロールできるようになる
    )

    for i in range(3):
        gv.controls.append(
            ft.Container(
                bgcolor=ft.colors.BLUE,
                border_radius=20,
            )
        )
    
    # GridViewの幅をmax_extent × controlsの長さに設定
    gv.width = gv.max_extent * len(gv.controls)

    page.add(ct)

ft.app(main)
```
**実行結果**
![](https://storage.googleapis.com/zenn-user-upload/eeee12fadf4f-20241130.png)

# 原理
なんで上記のようなコードにする必要があるのか、自分で気づくのに紆余曲折あったのでそれを記していこうと思います。
まず、これから解説する内容の元となるコードを紹介しておきます。

```python
import flet as ft

def main(page: ft.page):
    gv = ft.GridView(
        spacing=30,
        run_spacing=30,
        max_extent=200,
    )

    for i in range(3):
        gv.controls.append(
            ft.Container(
                bgcolor=ft.colors.BLUE,
                border_radius=20,
            )
        )

    page.add(gv)

ft.app(main)
```

GridViewに青色のContainerを3つだけ入れた単純なコードです。
`max_extent`を設定しておくことで、ウィンドウサイズを変更してもいい感じにGridViewが内包するコントロールの大きさを調整してくれるようになります。（今回はこの`max_exnten`ありきで書いていきます）

## GridViewに中央寄せをするプロパティはない

公式ドキュメントを読んだり、ChatGPTやGeminiに聞いてみたりしたのですが、FletのGridViewコントロールには`alignment`のような整列方法を設定するプロパティは存在しないようです。
そのため、今回のようにGridViewで中央寄せを実現したい場合は別のアプローチを考えなければいけません。

## Containerに入れて`alignment`を設定
GridView単体では中央寄せをできないのであれば、親要素で中央に寄せてやろう！ということで以下のようなコードを書いてみました。

```python
import flet as ft

def main(page: ft.page):
    gv = ft.GridView(
        spacing=30,
        run_spacing=30,
        max_extent=200,
    )

    ct = ft.Container(
        content=gv,     # GridViewを内包
        alignment=ft.alignment.center,  # Containerの内部を中央寄せ
    )

    for i in range(3):
        gv.controls.append(
            ft.Container(
                bgcolor=ft.colors.BLUE,
                border_radius=20,
            )
        )

    page.add(ct)

ft.app(main)
```

魂胆は単純で、GridViewをContainerに入れて、Container内部のレイアウトを中央寄せに設定しただけです。
これで解決！と思ったのですが実行してみるとあらびっくり

![](https://storage.googleapis.com/zenn-user-upload/41196ed763cf-20241130.png)

Containerに内包しないときと何も変わっていません。

なんで？？

## GridViewは横に延びる
どうやら、GridViewは親要素の領域の許す限り無制限に横方向にスペースを占拠する仕様のようです。（上下方向には不必要に延びない）
たとえば、内包する要素が3つだけの以下のようなGridViewでも、GridViewの占めるスペースは以下のようになっています。

![](https://storage.googleapis.com/zenn-user-upload/2a9586b4cb49-20241130.png)

そのため、親要素のContainerで中央寄せに設定しても場所が動かない、という状況に陥っていました。

:::message
ちなみに、Fletでは複数のコントロールを横並びにする**Row**なども同様の使用となっています。
:::

## GridViewの幅を設定する
これを解決するには、GridViewの領域を以下のように表示するコントロールにフィットさせる必要があります。

![](https://storage.googleapis.com/zenn-user-upload/915467770c13-20241130.png)

GridViewコントロールには`width`プロパティが用意されているので、これを適切な値に設定することができれば、問題を解決できそうです。
今回使用しているコードでは、**最大幅200（`max_extent=200`）のContainerが3つ並ぶ**というGridViewになっています。そのため、**600**をGridViewの幅として設定してあげればちょうど良さそうです。
ここで、冒頭の[**結論**](#%E7%B5%90%E8%AB%96)で述べた
```python
gv.width = gv.max_extent * len(gv.controls)
```
という計算式が出てきます。
GridViewの`controls`にコントロールを追加し終わったタイミングでこの計算式を実行するとGridViewの幅が希望通りに調整され、狙ったようにGridViewを中央揃えにできます！

**完成形コード**
```python
import flet as ft

def main(page: ft.page):
    gv = ft.GridView(
        spacing=30,
        run_spacing=30,
        max_extent=200,
    )

    ct = ft.Container(
        content=gv,     # GridViewを内包
        alignment=ft.alignment.center,  # Containerの内部を中央寄せ
        expand=1,       # expandを1に設定することでGridViewがスクロールできるようになる
    )

    for i in range(3):
        gv.controls.append(
            ft.Container(
                bgcolor=ft.colors.BLUE,
                border_radius=20,
            )
        )
    
    # GridViewの幅をmax_extent × controlsの長さに設定
    gv.width = gv.max_extent * len(gv.controls)

    page.add(ct)

ft.app(main)
```
:::message
**「GridViewに内包するコントロールの数が多いと、GridViewの幅が大きくなりすぎるのでは？」**
と思うかもしれまんせが、基本的にFletでは親要素（今回はContainer）のサイズに子要素は制限されるのでGridViewの`width`が大きくなっても問題にはなりません。
:::

# おまけ
結論でさらっとコメントアウトしてましたが、GridViewの親要素となるContainerの`expand`プロパティを`1`（または`True`）に設定しておくと、GridViewがウィンドウをはみ出るほど下方向に延びてもスクロールができるようになります。
```python
#~~~省略~~~#

ct = ft.Container(
    content=gv,
    alignment=ft.alignment.center,
    expand=1,       # expandを1に設定することでGridViewがスクロールできるようになる
)

#~~~省略~~~#
```

![292baa1e9cda-20241130.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822515/7767e1e0-a5d9-4593-aa3b-9dfdc4f23b5e.gif)


Fletはまだベータ版のライブラリということもあり情報も少ないので、すこし難しいことをしようとすると自力で工夫しなければいけいないことが多いですよね（それがFletの楽しいところでもありますが）。
この記事が少しでもあなたの役に立てば幸いです。