前回、Fletのアイコン(ft.Icons)がバージョンのたびに書き方を変えていて、AIに書かせるとAttributeErrorになる、という話を書きました。
アイコンの書き方さえ揃えば安心、と思っていたのですが、次に手を動かしてみて驚いたのは「レイアウトが崩れているのに、コンソールにもエラーにも何も出ない」ことでした。AIに「サイドバー付きのダッシュボードを作って」と頼んで出てきたコードは、見た目には自然。ですがブラウザで開くと、3枚並ぶはずのカードのうち1枚が画面からはみ出ていました。
この記事では、Fletのレイアウトがなぜエラーなしで崩れるのかという検証結果と、AIがよくやる崩し方のパターン、expandやResponsiveRowを使った直し方までを、動かした画面とあわせてまとめます。
この記事で分かること
- Fletの
Row/Columnの基本(横に並べる箱・縦に並べる箱) - Fletはレイアウトが崩れてもエラーもコンソール警告も出さないという検証結果
- AIが書いたコードでよくある崩れ方(
expand忘れ、深いネストでの消失) ResponsiveRowで画面幅に応じて自動的に列数を変える方法- 項目が多すぎるときに
scroll=ft.ScrollMode.AUTOで横スクロールにする方法 - 業務ダッシュボードを例にしたBefore/Afterの直し方
FletのRow/Column入門:横に並べる箱・縦に並べる箱
Fletでレイアウトを組む基本は、ft.Row(横に並べる)とft.Column(縦に並べる)の2つです。中に入れた部品を、指定した方向へ順番に並べていきます。
import flet as ft
from layout_common import setup_page
def box(label, color):
return ft.Container(
content=ft.Text(label, color=ft.Colors.WHITE, weight=ft.FontWeight.BOLD),
bgcolor=color,
width=110,
height=70,
alignment=ft.Alignment.CENTER,
border_radius=10,
)
def main(page: ft.Page):
setup_page(page, "Row Column Basic", bgcolor=ft.Colors.WHITE)
page.padding = 30
page.add(
ft.Text("ft.Row(...) : 横に並べる箱", size=14, color=ft.Colors.GREY_700),
ft.Row([box("A", ft.Colors.INDIGO), box("B", ft.Colors.INDIGO_300), box("C", ft.Colors.INDIGO_100)], spacing=12),
ft.Container(height=24),
ft.Text("ft.Column(...) : 縦に並べる箱", size=14, color=ft.Colors.GREY_700),
ft.Column([box("A", ft.Colors.TEAL), box("B", ft.Colors.TEAL_300), box("C", ft.Colors.TEAL_100)], spacing=12),
)
ft.run(main, view=ft.AppView.WEB_BROWSER, port=8561)
setup_pageは共通の初期設定(タイトル・テーマ・背景色)をまとめた自作の補助関数で、この後のサンプルでも使い回します。中身は次の通りです。
import flet as ft
def setup_page(page: ft.Page, title="Flet Layout Demo", bgcolor=None):
page.title = title
page.theme = ft.Theme(color_scheme_seed=ft.Colors.INDIGO)
page.bgcolor = bgcolor or ft.Colors.GREY_50ここまではCustomTkinterのpack(side="left")のような感覚に近く、迷うところはありません。問題はここからです。
Fletはレイアウトが崩れてもエラーもコンソール警告も出さない
サイドバー(幅220px)と、カード3枚(各幅300px)を横に並べただけの、次のシンプルなコードを動かしてみます。
import flet as ft
from layout_common import setup_page
def card(label, color):
return ft.Container(
width=300,
height=140,
bgcolor=color,
border_radius=10,
padding=14,
content=ft.Text(label, weight=ft.FontWeight.BOLD),
)
def main(page: ft.Page):
setup_page(page, "Overflow Demo", bgcolor=ft.Colors.WHITE)
page.padding = 0
sidebar = ft.Container(
width=220,
bgcolor=ft.Colors.INDIGO_100,
padding=20,
content=ft.Text("サイドバー", weight=ft.FontWeight.BOLD),
)
page.add(
ft.Row(
[
sidebar,
ft.Container(
padding=20,
content=ft.Column(
[
ft.Text("売上ダッシュボード", size=20, weight=ft.FontWeight.BOLD),
ft.Container(height=12),
ft.Row(
[
card("カード1: 今月の売上", ft.Colors.RED_100),
card("カード2: 新規顧客数", ft.Colors.GREEN_100),
card("カード3: 対応件数", ft.Colors.BLUE_100),
],
spacing=16,
),
]
),
),
],
)
)
ft.run(main, view=ft.AppView.WEB_BROWSER, port=8562)ブラウザで開くと、3枚目のカードが画面の右側で切れています。
ここで真っ先に疑うのは「エラーメッセージ」だと思います。ところが、ターミナルには何も出力されず、ブラウザの開発者コンソールを開いて確認しても手がかりがありませんでした。
Flutter本来のネイティブアプリ開発では、この手のはみ出しは”A RenderFlex overflowed by ○○ pixels”という黄色と黒の縞模様の警告表示でおなじみですが、Fletのweb表示ではこの警告自体が出ません。つまりFletのレイアウト崩れは、コードのエラーを追いかける方法では気づけず、目視でしか発見できないタイプの不具合だと言えます。AIに丸投げしたコードほど、この「一見動いているように見える」落とし穴にはまりやすくなります。
AIがよくやる崩し方
ここからは、AIが生成しがちなコードで実際に踏んだ、2つの典型的な崩れ方を紹介します。
expand忘れでコンテンツ側が余白を残したまま伸びない
サイドバー+メインコンテンツを横に並べる構成で、メインコンテンツ側にexpand=Trueを付け忘れると、コンテンツは中身の幅にしか広がらず、右側に不自然な余白が残ります。
Good側の該当部分は次の通りです。
ft.Container(
expand=True,
bgcolor=ft.Colors.INDIGO_50,
padding=14,
border=ft.Border.all(1, ft.Colors.INDIGO_200),
border_radius=8,
content=ft.Text("メインコンテンツ\n(expand=True)", size=12),
)expand=Trueを付けるだけの違いですが、これもエラーは出ません。見た目の違和感でしか気づけない点は、先ほどのはみ出しと同じです。
expand=Trueを深くネストすると、高さの基準がないまま要素ごと消える
Columnの中にexpand=TrueのRow、さらにその中にexpand=TrueのColumnとContainer……と入れ子にした状態で、一番外側の高さが確定していないと、内側のexpand要素は高さ0になって完全に消えます。
import flet as ft
from layout_common import setup_page
def main(page: ft.Page):
setup_page(page, "Nest Test", bgcolor=ft.Colors.WHITE)
page.padding = 20
page.add(
ft.Column(
[
ft.Text("深いネストの例", size=18, weight=ft.FontWeight.BOLD),
ft.Row(
[
ft.Column(
[
ft.Text("左"),
ft.Container(expand=True, bgcolor=ft.Colors.RED_100),
],
expand=True,
),
ft.Column(
[
ft.Text("右"),
ft.Container(expand=True, bgcolor=ft.Colors.BLUE_100),
],
expand=True,
),
],
expand=True,
),
],
)
)
ft.run(main, view=ft.AppView.WEB_BROWSER, port=8564)
直し方は、高さの基準になるコンテナに明示的なheightを与えることです。
ft.Container(
height=380, # 高さの基準を明示することで、内側のexpandが効くようになる
content=ft.Row(
[...],
expand=True,
),
),
「ネストが深いと崩れやすい」というのは感覚的な注意点ではなく、こうして手元で再現できる具体的な不具合です。AIはexpand=Trueを機械的に足すことはできても、「どこかに高さの基準が必要」という設計上の前提までは考慮してくれないことが多いようです。
直し方:ResponsiveRowとscrollで崩れにくい設計にする
固定のwidthで組むこと自体が崩れの根っこなので、ここでは「幅を決め打ちしない」2つの選択肢を紹介します。
ResponsiveRowで画面幅に応じて自動的に列数を変える
ft.ResponsiveRowは、各要素のcolプロパティに画面幅ごとの列数を指定しておくと、ブラウザの幅に応じて自動的に折り返してくれるコンポーネントです。
import flet as ft
from layout_common import setup_page
LABELS = [
("今月の売上", ft.Colors.RED_100),
("新規顧客数", ft.Colors.GREEN_100),
("対応件数", ft.Colors.BLUE_100),
("在庫アラート", ft.Colors.ORANGE_100),
("未読メッセージ", ft.Colors.PURPLE_100),
("完了タスク", ft.Colors.TEAL_100),
]
def card(label, color):
return ft.Container(
col={"xs": 12, "sm": 6, "md": 4},
bgcolor=color,
border_radius=10,
padding=16,
height=100,
content=ft.Text(label, weight=ft.FontWeight.BOLD),
)
def main(page: ft.Page):
setup_page(page, "Responsive Demo", bgcolor=ft.Colors.WHITE)
page.padding = 20
page.add(
ft.Text("業務ダッシュボード(ResponsiveRow)", size=18, weight=ft.FontWeight.BOLD),
ft.Container(height=10),
ft.ResponsiveRow([card(label, color) for label, color in LABELS], spacing=14, run_spacing=14),
)
ft.run(main, view=ft.AppView.WEB_BROWSER, port=8566)col={"xs": 12, "sm": 6, "md": 4}は「画面が最も狭い(xs)ときは12/12=1列、少し広い(sm)ときは6/12=2列、さらに広い(md)ときは4/12=3列」という意味です。ブラウザの幅だけを変えて撮ったのが次の3枚で、幅に応じて3列→2列→1列と並び替わっています。
項目が多すぎるときはscroll=ft.ScrollMode.AUTOで横スクロールにする
画面に収めきれないほど項目が多い場合、無理に折り返さず、横スクロールに任せるという選択肢もあります。ft.Rowにscroll=ft.ScrollMode.AUTOを付けるだけです。
ft.Row(
[card(f"項目{i+1}", COLORS[i]) for i in range(5)],
spacing=14,
scroll=ft.ScrollMode.AUTO,
)
崩れを隠すのではなく、「はみ出す設計」にしたい場合に使える方法です。
AIへの丸投げで崩れたレイアウトを直す4ステップ
ここまでの内容を踏まえて、AIが生成したレイアウトの崩れを手元で直していく一連の流れを4ステップで見てみます。
ポイントは、AIが出したwidth=300のような固定値そのものを疑うことです。widthを指定しているだけのコードは一見自然で、AIも人間も違和感を覚えにくいのですが、それこそが崩れの火種です。プロンプトを工夫する場合は、「幅は固定値ではなくexpandやResponsiveRowで組んで」と明記すると、この手の崩れを事前に防ぎやすくなります。
実践:業務ダッシュボードをBefore/Afterで直す
最後に、ここまでの内容をまとめて、サイドバー付きの業務ダッシュボードをBefore/Afterで直してみます。
Beforeは、幅を固定値で決め打ちしたコードです(冒頭の崩壊デモと同じ構成)。
Afterは、サイドバー以外の幅をexpand=Trueにし、カード部分をResponsiveRowに組み替えたものです。
import flet as ft
from layout_common import setup_page
CARDS = [
("今月の売上", ft.Colors.RED_100),
("新規顧客数", ft.Colors.GREEN_100),
("対応件数", ft.Colors.BLUE_100),
]
def card(label, color):
return ft.Container(
col={"xs": 12, "sm": 6, "md": 4},
bgcolor=color,
border_radius=10,
padding=16,
height=120,
content=ft.Text(label, weight=ft.FontWeight.BOLD),
)
def main(page: ft.Page):
setup_page(page, "Dashboard Fixed", bgcolor=ft.Colors.WHITE)
page.padding = 0
sidebar = ft.Container(
width=220,
bgcolor=ft.Colors.INDIGO_100,
padding=20,
content=ft.Text("サイドバー", weight=ft.FontWeight.BOLD),
)
main_area = ft.Container(
expand=True, # 幅の決め打ちをやめて残り幅いっぱいに広げる
padding=20,
content=ft.Column(
[
ft.Text("売上ダッシュボード", size=20, weight=ft.FontWeight.BOLD),
ft.Container(height=12),
ft.ResponsiveRow( # 固定Rowをやめて画面幅で列数が変わるResponsiveRowに
[card(label, color) for label, color in CARDS],
spacing=16,
run_spacing=16,
),
]
),
)
page.add(ft.Row([sidebar, main_area], spacing=0))
ft.run(main, view=ft.AppView.WEB_BROWSER, port=8568)
サイドバーの幅だけは業務ツールらしく固定のままにし、それ以外を「決め打ちしない」設計に変えるだけで、画面幅を問わず崩れにくいダッシュボードになります。
サンプルコードのダウンロード
この記事で紹介した9本のサンプルコード(Row/Column基本、overflowデモ、expand比較、ネスト崩壊/修正、ResponsiveRow、scroll、実践ダッシュボード、共通ヘルパー)は、まとめてダウンロードできます。
📦 サンプルコード一式をダウンロード(zip)よくある質問(FAQ)
QFletでレイアウトが崩れているのに気づく方法はありますか?
A現状ではエラーメッセージやコンソール警告に頼れないため、ブラウザで画面を開いて目視で確認するしかありません。特に固定のwidthを多用しているコードは、画面幅を変えて崩れないか一度試してみると安心です。
Qexpand=Trueはどこに付ければいいですか?
A「残りの幅(または高さ)いっぱいに広げたい要素」に付けます。Rowの中で幅を分け合いたい要素、Columnの中で高さを分け合いたい要素が対象です。ただし、外側に高さや幅の基準がまったくない状態でネストして使うと、要素が消えることがあるので注意してください。
QResponsiveRowのcolはどう決めればいいですか?
Acol={"xs": 12, "sm": 6, "md": 4}のように、12を基準にした分数で考えます。1行に何列並べたいかで割り算し、12 ÷ 列数を各ブレークポイントに当てはめると分かりやすいです(3列なら12÷3=4)。
Qwidthとexpandはどう使い分ければいいですか?
Aサイドバーやアイコンのように「常に同じ幅であってほしい」要素にはwidth、メインコンテンツのように「残りの幅を使い切ってほしい」要素にはexpand=Trueを使うのが基本の考え方です。
QAIにレイアウトのコードを書かせるとき、崩れを防ぐコツはありますか?
A「幅は固定値ではなくexpandやResponsiveRowで組んで」のように、決め打ちのwidthを避ける指示を加えるだけでも、崩れの一部は事前に防げます。ただし出力されたコードは必ずブラウザで目視確認してください。
まとめ
この記事では、Fletのレイアウトがエラーなしで崩れる理由と、AIがよくやる崩し方のパターン、ResponsiveRowやscrollを使った直し方までを、動かした画面とあわせて紹介しました。
- Fletのレイアウトは
Row(横並び)とColumn(縦並び)の組み合わせが基本 - レイアウトが崩れても、Fletはエラーもコンソール警告も出さない。目視でしか気づけない
expand忘れ、高さの基準がないままの深いネストが、AIコードでよくある崩れ方ResponsiveRowは画面幅に応じて自動的に列数を変えてくれる- 項目が多すぎる場合は
scroll=ft.ScrollMode.AUTOで横スクロールにする選択肢もある - widthの決め打ちを避け、expand/ResponsiveRowで組むよう意識するだけで、崩れにくいレイアウトになる
Flet Series Navigation
Fletシリーズ(全4回):AIが壊すFletコードとその直し方
【保存版】Fletアイコン一覧&使い方|バージョンで名前が変わってAIが誤爆する理由
→Fletのレイアウトが崩れてもエラーが出ない理由|Row/Column・expand・ResponsiveRowで直す方法
この記事Fletで画面遷移が動かない理由|page.go廃止とft.View引数順の罠、TemplateRouteの使い方
→ 4【無料配布】Flet 1.0テンプレート集|基本構成+Excel取込対応の社内ツールひな形
→Follow me on X
本ブログの中の人「ニンジン🥕」はXやってます。新着記事はいち早くポストでお知らせしているので、見逃したくない方は @ninjin_py_vba をフォローしてみてください🥕
@ninjin_py_vba をフォロー


コメント