python-flet について
python-fletは、Fletを使った開発のためのスキルです。FletはFlutterウィジェットをPythonコントロールとして扱うフレームワークで、Web、デスクトップ、モバイルアプリに対応し、アプリ構成やUI、ナビゲーション、非同期処理、性能改善を扱います。
主な機能
- 01Pythonコントロールによるアプリ構築
- 02レイアウトとカスタムコントロールの作成
- 03ルーティングとテーマの設定
- 04非同期タスクとライフサイクルメソッドの利用
- 05大規模なスクロールリストの最適化
- 06Webまたはデスクトップでの実行
活用シーン
- 01Pythonでクロスプラットフォームアプリを開発
- 02カスタムコントロールを使ったルーティング画面の構築
- 03大規模なリストの効率的な表示
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ the-perfect-developer/ the-perfect-opencode --skill python-fletインストール後、Claude Code で/python-fletを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/counter-app.pycustom-control.pyreferences/controls-and-layout.mdpatterns-and-best-practices.md詳細
Python Flet 開発
Flet は、フロントエンドの経験がなくても、クロスプラットフォームのウェブ、デスクトップ、モバイルアプリを構築できる Python フレームワークです。Flutter ウィジェットをラップし、Python コントロールとして公開します。
アプリのエントリーポイント
すべての Flet アプリには、ft.Page を受け取る main 関数があり、最後に ft.run(main) を呼び出します。
import flet as ft
def main(page: ft.Page):
page.title = "My App"
page.add(ft.Text("Hello, Flet!"))
ft.run(main)
インストール:pip install 'flet[all]'
デスクトップで実行:flet run main.py
ウェブで実行:flet run --web main.py
ホットリロード(ディレクトリを再帰的に監視):flet run --recursive main.py
プロジェクト構成
my-app/
├── pyproject.toml
└── src/
├── assets/
│ └── icon.png
└── main.py
次のコマンドで作成します:flet create(または uv run flet create)
基本概念
Page
ft.Page はルートコンテナーです。コントロールを追加する前に設定します。
def main(page: ft.Page):
page.title = "App Title"
page.theme_mode = ft.ThemeMode.LIGHT
page.theme = ft.Theme(color_scheme_seed=ft.Colors.BLUE)
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.scroll = ft.ScrollMode.AUTO
コントロールと update()
コントロールは Python オブジェクトです。変更を反映するには page.update() または control.update() が必要です。
text = ft.Text("Initial")
page.add(text)
def change(e):
text.value = "Changed"
text.update() # prefer: update only this control
# page.update() # fallback: updates entire page
ルール:page.update() よりも control.update() を優先してください。より小さな差分を送信できます。
レイアウトコントロール
| コントロール | 用途 |
|---|---|
ft.Column | 縦方向に積み重ねます |
ft.Row | 横方向に並べます |
ft.Stack | 絶対位置指定 |
ft.Container | パディング、色、枠線、アニメーションを設定できるボックス |
ft.ListView | 効率的な縦方向/横方向のスクロール可能リスト |
ft.GridView | 効率的なスクロール可能グリッド |
page.add(
ft.Row(
controls=[ft.Text("Left"), ft.Text("Right")],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
)
)
Expand:expand=True を指定すると、利用可能なスペースを埋めます。
ft.ListView(expand=True, spacing=10)
カスタムコントロール
スタイル付きコントロール(単一の Flet コントロールを継承)
@ft.control
class PrimaryButton(ft.Button):
bgcolor: ft.Colors = ft.Colors.BLUE_700
color: ft.Colors = ft.Colors.WHITE
@ft.control または @dataclass デコレーターを使用します。フィールドの型には注釈が必要です。
複合コントロール(複数のコントロールを組み合わせる)
@ft.control
class TaskItem(ft.Row):
text: str = ""
def init(self):
self.checkbox = ft.Checkbox()
self.label = ft.Text(value=self.text)
self.controls = [self.checkbox, self.label]
def toggle(self, e):
self.label.color = ft.Colors.GREY if self.checkbox.value else None
self.update()
ライフサイクルメソッド
| メソッド | 呼び出されるタイミング | 用途 |
|---|---|---|
init() | __init__ の後 | サブコントロールのセットアップ |
build() | self.page が割り当てられたとき | プラットフォームに応じたロジック |
did_mount() | ページに追加された後 | タイマーの開始、データの取得 |
will_unmount() | 削除される前 | クリーンアップ、タスクのキャンセル |
before_update() | update() のたび | 派生状態の同期 |
分離ルール:独自のメソッド内で self.update() を呼び出すカスタムコントロールでは、is_isolated = True を設定する必要があります(または is_isolated プロパティをオーバーライドして True を返します)。
ナビゲーションとルーティング
単一の信頼できる情報源として、page.on_route_change と page.views を使用します。
def main(page: ft.Page):
def route_change():
page.views.clear()
page.views.append(ft.View("/", controls=[...]))
if page.route == "/settings":
page.views.append(ft.View("/settings", controls=[...]))
page.update()
async def view_pop(e):
page.views.remove(e.view)
await page.push_route(page.views[-1].route)
page.on_route_change = route_change
page.on_view_pop = view_pop
route_change()
ft.run(main)
ナビゲート:await page.push_route("/settings")
パラメーター付きルート:
troute = ft.TemplateRoute(page.route)
if troute.match("/items/:id"):
print(troute.id)
ルーティングのルール:
page.views内にルート/のビューを常に保持します- すべてのルートロジックを
page.on_route_changeに集約します - 状態を同期させるため、常に
page.on_view_popを処理します
テーマ設定
# App-wide theme
page.theme = ft.Theme(color_scheme_seed=ft.Colors.GREEN)
page.dark_theme = ft.Theme(color_scheme_seed=ft.Colors.BLUE)
page.theme_mode = ft.ThemeMode.SYSTEM # LIGHT | DARK | SYSTEM
# Nested theme (scoped to a container)
ft.Container(
theme=ft.Theme(color_scheme=ft.ColorScheme(primary=ft.Colors.PINK)),
content=ft.Button("Pink button"),
)
非同期アプリ
asyncio をサポートするには、main を async にします。time.sleep() ではなく asyncio.sleep() を使用します。
import asyncio
import flet as ft
async def main(page: ft.Page):
async def on_click(e):
await asyncio.sleep(1)
page.add(ft.Text("Done!"))
page.add(ft.Button("Click me", on_click=on_click))
ft.run(main)
did_mount() からバックグラウンドのコルーチンを実行するには、page.run_task(coro) を使用します。
パフォーマンス:大規模なリスト
数百件のアイテムを扱う場合は、Column/Row ではなく ListView または GridView を使用します。
# Efficient: renders only visible items
lv = ft.ListView(expand=True, spacing=10, item_extent=50)
for i in range(5000):
lv.controls.append(ft.Text(f"Line {i}"))
page.add(lv)
# Batch updates to avoid large WebSocket messages
for i in range(5000):
lv.controls.append(ft.Text(f"Line {i}"))
if i % 500 == 0:
page.update()
page.update()
暗黙的なアニメーション
コントロールの animate_* プロパティを設定して有効にします。
# Opacity fade
container = ft.Container(
width=150, height=150,
bgcolor=ft.Colors.BLUE,
animate_opacity=300, # ms
)
# Scale with bounce curve
ft.Container(
animate_scale=ft.Animation(
duration=600,
curve=ft.AnimationCurve.BOUNCE_OUT,
)
)
# Position animation (inside Stack or page.overlay)
ft.Container(animate_position=1000)
# Animated content switcher
ft.AnimatedSwitcher(
content,
transition=ft.AnimatedSwitcherTransition.SCALE,
duration=500,
)
ウィンドウ制御(デスクトップ)
page.title = "My App"
page.window.width = 800
page.window.height = 600
page.window.resizable = True
page.window.always_on_top = False
page.window.center()
クイックリファレンス:よく使うコントロール
ft.Text("Hello", size=20, weight=ft.FontWeight.BOLD)
ft.Button("Click", on_click=handler)
ft.IconButton(ft.Icons.ADD, on_click=handler)
ft.TextField(label="Name", on_change=handler)
ft.Checkbox(label="Check me", on_change=handler)
ft.Dropdown(options=[ft.dropdown.Option("A"), ft.dropdown.Option("B")])
ft.Image(src="photo.jpg") # from assets/
ft.Image(src="https://example.com/img.png") # remote
ft.AppBar(title=ft.Text("Title"))
ft.NavigationDrawer(controls=[...])
ft.AlertDialog(title=ft.Text("Alert"), content=ft.Text("Body"))
追加リソース
リファレンスファイル
references/controls-and-layout.md- レイアウトパターン、Container のスタイル設定、Stack の位置指定、コントロールのプロパティを網羅していますreferences/patterns-and-best-practices.md- 状態管理、セッション/クライアントストレージ、PubSub、キーボードショートカット、エラー処理
サンプルファイル
examples/counter-app.py- ページのセットアップとイベントハンドラーを示す、最小構成の動作するカウンターexamples/custom-control.py- ライフサイクルメソッドと分離を備えた複合カスタムコントロール