Fletは、画面をPythonのコードだけで書き、同じコードからWeb・デスクトップ(Windows・macOS・Linux)・モバイル(iOS・Android)のアプリを作れるオープンソースのフレームワークです。描画はGoogleのUIツールキットFlutterが担うため、DartやHTML・CSS・JavaScriptを書く必要はありません。2026年9月15日に初の安定版となるFlet 1.0が出て、起動の書き方やボタンの名前が変わりました。本記事は2026年10月時点の最新版1.0.4で、インストールから最小アプリ、アプリの終了方法、古いコードの移行までを、実際に動かしたコードで説明します。
まとめ:Fletの要点と1.0で変わったこと
- FletはPythonだけでWeb・デスクトップ・モバイルのアプリを書けるUIフレームワーク。描画はFlutter、ライセンスはApache License 2.0。
- 最新版は1.0.4(2026年10月8日公開)。動作にはPython 3.10以上が必要。
- 導入は仮想環境で
pip install 'flet[all]'、またはuvでuv add flet。起動はflet runでホットリロードが効く。 - 1.0では起動が
ft.app(target=main)からft.run(main)へ、ft.ElevatedButtonがft.Buttonへ変わり、ft.UserControlは削除された。これらの旧APIを使った入門記事のコードは、そのままでは動かない。 - デスクトップアプリのウィンドウを閉じるのは
await page.window.close()。終了確認を出すならpage.window.prevent_close = Trueにして閉じる要求を受け取り、確認後にdestroy()する。 - 1.0ではイベント処理が1本のイベントループで動くため、
time.sleep()や同期の通信を書くと画面が固まる。asyncioか別スレッドへ逃がす。
Fletとは:PythonでFlutterの画面を組み立てるフレームワーク
Fletの意味とできること
Fletは、ボタン・テキスト入力・表・ダイアログなどのUI部品(Fletでは「コントロール」と呼ぶ)をPythonのオブジェクトとして組み立てるだけで、アプリの画面を作れるフレームワークです。開発元はAppveyor Systemsで、GitHubのflet-dev/fletは2026年10月18日時点で約1.74万スターです。公式サイトは150以上のコントロールとサービスを提供すると説明しています。
向いているのは、データ入力フォーム、社内向けの集計ツール、機械学習モデルの操作画面など、Pythonで書いた処理に画面を付けたい場面です。1.0ではNumPy・pandas・Pillowなど100を超えるパッケージがiOS・Android向けにビルド済みで提供されており、依存パッケージが対象のOSとPythonの版に対応していれば、Pythonの資産をスマートフォンのアプリにも載せられます。
FletとFlutterの違い
FlutterはDartで書くUIツールキットで、FletはそのFlutterをPythonから操作する仕組みです。開発者が書くのはPythonだけで、画面の描画はFletに組み込まれたFlutterが行います。Flet 1.0.4の flet --version を実行すると、対応するFlutterの版として3.44.8が表示されました。Flutter本体の最新動向はFlutter 3.47とは?material_ui移行・iOS 15と3.47.7までの修正【2026年10月時点】で扱っています。
| 観点 | Flet | Flutter |
|---|---|---|
| 書く言語 | Python | Dart |
| 描画エンジン | Flutter(同梱) | Flutter |
| 出力先 | Web・デスクトップ・iOS・Android | Web・デスクトップ・iOS・Android |
| Flutter SDKの導入 | ビルド時に自動で導入 | 自分で導入 |
| 向く用途 | Python資産の画面化・社内ツール | 本格的な一般向けアプリ |
動作要件と対応OS
公式のインストール手順によると、FletはPython 3.10以上を要求します。開発に使えるOSは、macOS 12(Monterey)以降、64ビット版のWindows 10・11、Debian 10〜12とUbuntu 20.04・22.04・24.04 LTSです。
Fletのインストール(pip・uv)
pipと仮想環境でのインストール
pipで入れる場合は、アプリ用のフォルダに仮想環境を作り、有効化してからインストールします。仮想環境の考え方は仮想環境とは?venv・コンテナ・仮想マシンの違いと隔離レベルの選び方を実装者向けに解説にまとめています。
mkdir my-app
cd my-app
python -m venv .venv
source .venv/bin/activate # Windowsは .venv\Scripts\activate
pip install 'flet[all]'
flet doctor # 環境の確認
flet[all] は次の4パッケージをまとめて入れる指定です。配布するアプリに必要なのはライブラリ本体の flet だけで、残りは開発用のツールです。
| パッケージ | 役割 |
|---|---|
| flet | import するライブラリ本体と flet コマンド |
| flet-cli | create・run・build・test などのサブコマンド |
| flet-desktop | flet run で開くデスクトップクライアント |
| flet-web | flet run -w で使うWebクライアントとサーバー |
uvでのインストール
uvで既存のプロジェクトに足すなら、実行時の依存に flet、開発用の依存にツール群を入れます。uvとPoetryの選び方はPoetryとは?Python依存管理の仕組みと2.5系の使い方・uvとの選び方を参照してください。
uv add flet
uv add --dev flet-cli flet-desktop flet-web 'flet[test]'
uv run flet run
入れた版は flet --version で確認できます。更新は pip install 'flet[all]' --upgrade または uv add flet --upgrade です。
Fletアプリの作成と起動
flet createでの雛形作成とflet runでの起動
flet create アプリ名 を実行すると、pyproject.toml・src/main.py・src/assets/icon.pngに、pytest用の tests/test_main.py を加えた雛形ができます(1.0.4で生成して確認)。この構成は後で flet build による配布にそのまま使えます。起動は flet run で、ファイルを保存すると画面に反映されるホットリロードが既定で働きます。
| コマンド | 動き |
|---|---|
flet run main.py |
デスクトップのウィンドウで起動 |
flet run -w main.py |
Webアプリとして起動しブラウザで開く |
flet run -d main.py |
スクリプトのフォルダを監視して再読み込み |
flet run -r main.py |
サブフォルダまで再帰的に監視 |
flet run --android |
Androidの実機で起動 |
最小アプリ:ft.runとボタンのイベント
Flet 1.0では、ページを受け取る main 関数を書き、最後に ft.run(main) で起動します。次のコードは、ボタンを押すと文字が変わるアプリです。
import flet as ft
def main(page: ft.Page):
page.title = "Hello Flet"
text = ft.Text("クリック前")
def on_click(e):
text.value = "クリックされました"
page.add(text, ft.Button("押す", on_click=on_click))
ft.run(main)
page はアプリの表示領域で、page.add() に渡したコントロールが上から並びます。1.0ではイベント処理と main の終わりに画面の更新が自動で走るため、0.x時代のコードに必ずあった page.update() は書かなくても反映されます(公式のAuto-update解説)。
宣言的UI:@ft.componentとuse_state
1.0から、画面を状態から組み立てる宣言的UIも正式に使えます。Reactの useState に近い書き方で、状態を変えると関係する部分だけが描き直されます。
import flet as ft
@ft.component
def Counter():
count, set_count = ft.use_state(0)
return ft.Row([
ft.Text(f"押した回数: {count}"),
ft.Button("+1", on_click=lambda: set_count(count + 1)),
])
def main(page: ft.Page):
page.render(Counter)
ft.run(main)
上の「最小アプリ」のようにコントロールの値を直接書き換える命令的な書き方も1.0で引き続き使えます。公式の比較記事は、命令的な書き方は小さなアプリなら十分だが、画面が大きくなると連動するコントロールの同期処理があちこちのハンドラーに重複しやすい、と説明しています。画面が育ってきたら宣言的へ移るのが目安です。本文のアプリ例5本(最小アプリ・カウンター・終了ボタン・終了確認・非同期取得)は、Flet 1.0.4(Python 3.12)でWebサーバーとして起動し、main が最後まで実行されることを確認しました。クリック操作とデスクトップでのウィンドウ終了の動作は確認していません。
Fletアプリを終了する・ウィンドウを閉じる方法
ボタンからのデスクトップアプリの終了
デスクトップアプリをコードから閉じるには page.window.close() を使います。1.0ではウィンドウ操作のメソッドが非同期なので、async def のハンドラーから await を付けて呼びます。
import flet as ft
def main(page: ft.Page):
async def quit_app(e):
await page.window.close()
page.add(ft.Button("アプリを終了", on_click=quit_app))
ft.run(main)
close() はウィンドウの閉じるボタンを押したのと同じ要求を送ります。一方 page.window.destroy() はウィンドウをその場で破棄します。page.window はmacOS・Windows・Linuxのデスクトップ専用のAPIで、Webやモバイルのアプリには効きません。なお page.on_close はウィンドウを閉じたときのイベントではなく、Webアプリのセッションが期限切れ(既定60分)になったときのイベントなので、終了処理には使えません。
ウィンドウを閉じる前の確認ダイアログ
未保存のデータがあるアプリでは、page.window.prevent_close = True で閉じる要求を横取りし、page.window.on_event で ft.WindowEventType.CLOSE を受けて確認を出します(公式のWindowリファレンス)。
import flet as ft
def main(page: ft.Page):
page.window.prevent_close = True
async def yes(e):
# 保存などの後始末はここで済ませてから破棄する
await page.window.destroy()
def no(e):
page.pop_dialog()
confirm = ft.AlertDialog(
modal=True,
title=ft.Text("終了しますか?"),
actions=[ft.Button("はい", on_click=yes), ft.TextButton("いいえ", on_click=no)],
)
def on_window_event(e: ft.WindowEvent):
if e.type == ft.WindowEventType.CLOSE:
page.show_dialog(confirm)
page.window.on_event = on_window_event
page.add(ft.Text("ウィンドウの閉じるボタンを押すと確認が出ます"))
ft.run(main)
確認後は close() ではなく destroy() で閉じます。prevent_close が有効なままなので、close() を呼ぶと再び閉じる要求として横取りされるためです。保存処理は「はい」のハンドラーで destroy() の前に済ませます。公式の配布ガイドは、flet build で作ったアプリでは atexit や __del__ が終了時に実行される保証がないとしています。終了処理を atexit に任せる書き方は避けてください。
開発中のFletプロセスの停止
flet run や python main.py でターミナルから起動したアプリは、ウィンドウを閉じるか、ターミナルで Ctrl+C を押すとプロセスごと止まります。flet run -w で起動したWebアプリはブラウザのタブを閉じてもサーバーが動き続けるので、ターミナル側で止めます。
Flet 1.0の変更点と0.28からの移行
Flet 1.0までのリリース経緯と互換性方針
Flet 1.0は2026年9月15日の公式ブログで発表されました。公式の移行ガイドによれば、1.0は0.28.3から枝分かれして開発されたため、0.29〜0.69は存在しません。
| 版 | 位置づけ |
|---|---|
| 0.28.3 | 0.x系の最終版(不具合と脆弱性の修正のみ継続) |
| 0.70.0 | 1.0 Alpha:新アーキテクチャ・宣言的UI |
| 0.80.0 | 1.0 Beta:PyPIの最新版が0.28.3から交代 |
| 0.86.0 | パッケージングの刷新・Python 3.14を既定で同梱 |
| 1.0.0 | 安定版(2026年9月15日) |
| 1.0.4 | 2026年10月8日公開の最新安定版 |
1.0からは、APIを削除する前に原則としてマイナー版3回分の非推奨期間を置く互換性ポリシーが適用されます。ただし0.28からの移行はこのポリシーの対象外で、移行ガイドは1.0を「そのまま置き換えられる版ではない」と明記しています。
書き換えが必要な主なAPI
0.28までのAPIを使ったコードを1.0.4で実行すると、次のエラーで止まります。左の書き方を右に直せば多くは動きます。
| 0.28まで | 1.0 | 1.0.4で出たエラー |
|---|---|---|
ft.app(target=main) |
ft.run(main) |
AttributeError |
ft.ElevatedButton(...) |
ft.Button(...) |
AttributeError |
ft.Button(text="保存") |
ft.Button(content="保存") |
TypeError |
ft.colors.RED |
ft.Colors.RED |
AttributeError |
ft.icons.ADD |
ft.Icons.ADD |
AttributeError |
page.open(dialog) |
page.show_dialog(dialog) |
AttributeError |
page.go(route) |
await page.push_route(route) |
AttributeError |
page.client_storage |
ft.SharedPreferences() |
AttributeError |
起動行の ft.app(target=main) は、手元の1.0.4では AttributeError: module 'flet' has no attribute 'app' になりました。移行ガイドには TypeError: 'module' object is not callable と書かれています。flet.app は run() を定義するモジュールとして残っており、先に読み込まれていると後者になります。どちらが出ても直し方は同じです。
再利用部品を作る ft.UserControl も削除されました。1.0のコントロールはdataclassなので、元にするコントロールを継承し、@ft.control を付けて型注釈付きのフィールドを宣言します。__init__ の代わりに init() を書き、super().__init__() は呼びません。
@ft.control
class Task(ft.Row):
text: str = ""
def init(self):
self.controls = [ft.Checkbox(), ft.Text(self.text)]
シングルスレッド化で画面が固まるときの対処
移行ガイドが「最も重要な挙動の変更」と呼ぶのがここです。0.28では同期のイベントハンドラーがスレッドプールで動いていたため、ハンドラーの中で time.sleep() や requests.get() を呼んでも画面は固まりませんでした。1.0ではハンドラーがアプリのイベントループ上で直接動きます。その間は他のイベントも画面更新も止まり、デスクトップではOSがウィンドウを「応答なし」と表示することがあります。
移行後に「スピナーが出ない」「処理が終わってから一度に表示される」と感じたら、まずイベントハンドラーの中に time.sleep()・requests.get()・sqlite3 の問い合わせ・subprocess.run() のような同期の待ち処理が無いかを探します。見つかったら、待ち時間のある処理は async def にして非同期のライブラリを使うか、asyncio.to_thread() で別スレッドへ逃がします。
import asyncio
import json
import urllib.request
import flet as ft
def fetch_latest():
with urllib.request.urlopen("https://pypi.org/pypi/flet/json") as r:
return json.load(r)["info"]["version"]
def main(page: ft.Page):
result = ft.Text("未取得")
async def load(e):
# 通信を別スレッドへ逃がし、イベントループ(画面更新)を止めない
result.value = await asyncio.to_thread(fetch_latest)
page.add(ft.Button("Fletの最新版を調べる", on_click=load), result)
ft.run(main)
取得関数 fetch_latest() は、2026年10月18日の実行で「1.0.4」を返しました。time.sleep(1) は await asyncio.sleep(1)、threading.Thread で回していた常駐処理は page.run_task() に置き換えるのが公式の推奨です。非同期ライブラリの選び方はanyio・Trio・asyncioの違いと使い分け|Python非同期ライブラリ比較が参考になります。PyodideでブラウザだけでPythonを動かす静的Web版はスレッドを使えないので、asyncio.to_thread() も使えません。重い処理は分割するかサーバー側へ移します。
すぐに移行できない場合
依存ライブラリの都合で書き換えられないときは、flet==0.28.3 に固定すれば従来どおり動きます。0.28系は新機能の追加こそ止まっていますが、不具合と脆弱性の修正は続いています。新しく書くコードは1.0に合わせてください。
Fletアプリのビルドと配布(flet build)
ビルドできるOSの組み合わせ
配布用のパッケージは flet build 対象 で作ります。ビルドにはFlutter SDKが必要ですが、PATHに対応版が無ければ初回ビルド時に $HOME/flutter/ の下へ自動で導入されます。どのOSで何をビルドできるかは公式の対応表で決まっています。
| ビルドするOS | apk・aab | ipa | macos | linux | windows | web |
|---|---|---|---|---|---|---|
| macOS | ○ | ○ | ○ | × | × | ○ |
| Windows | ○ | × | × | ○(WSL) | ○ | ○ |
| Linux | ○ | × | × | ○ | × | ○ |
iOSとmacOSのアプリはMacでしか作れず、Fletのビルドの最低要件はXcode 15以降とCocoaPods 1.16以降です。ただしiOSアプリをApp Store Connectへアップロードするには、Appleの要件により2026年4月28日以降はXcode 26以降とiOS 26 SDKでのビルドが必要です。Windows版はVisual Studio 2022か2026の「C++によるデスクトップ開発」ワークロード、Android版はJDK 17とAndroid SDKが前提です。アプリに同梱するPythonは既定で3.14で、ネイティブ拡張の都合があれば --python-version 3.12 のように指定できます。
Webに出す2つの方式
Webには、PythonをサーバーのCPythonで動かす動的Webアプリと、PyodideでブラウザだけでPythonを動かす静的Webサイトの2通りで出せます。静的サイトは flet build web か flet publish で生成し、静的ファイルとして配信できます。flet publish はFlutterを必要とせず、依存パッケージをブラウザでの実行時に取得します。PyPIのパッケージはPythonだけで書かれたものかEmscripten向けのwheelがあるものに限られ、前述のとおりスレッドも使えないので、重い計算や秘密情報を扱う処理はサーバー側の方式を選びます。
Fletを選ぶべき場面・避ける場面
Pythonで書いた処理に、デスクトップかスマートフォンで動く画面を付けたいならFletを第一候補にしてよいと考えます。同じコードでWindows・macOS・iOS・Androidに出せる選択肢はPythonでは少なく、1.0でpytestによる結合テスト(flet test)まで揃いました。社内配布の業務ツール、現場のタブレットで使う入力アプリ、データ分析結果の閲覧アプリが典型です。複数のOS向けにアプリを作る体制づくりから相談したい場合は、弊社のFlutter / React Native開発でも支援しています。
逆に、次の条件ではFletを選ばない方が安全です。
- ブラウザで見せる分析ダッシュボードだけが目的:Streamlitの方が短く書ける。違いはStreamlitとは?できること・使い方・料金を実例コードで解説【Python】を参照。
- OS標準の見た目と細かいウィンドウ制御が要るデスクトップ専用アプリ:Qtの部品が揃うPySide6が向く。PySide6とは?ライセンス・商用利用・インストール・使い方まで【6.12対応】で比較できる。
- ストアで長期運用する一般向けアプリで、Dartを書けるチームがいる:Flutterを直接使う方が、Flutterの新機能やパッケージをすぐ使える。
- 同期処理を多用した0.28系の大きなアプリ:1.0への移行で、シングルスレッド化への対応がまとまった工数になる。移行ガイドを読んで見積もってから着手する。
よくある質問
FletとFlutterの違いは何ですか?
書く言語が違います。FlutterはDartで書くUIツールキットで、FletはそのFlutterをPythonから使う仕組みです。Fletでは開発者はPythonだけを書き、描画は同梱のFlutterが行います。
Fletはどの言語で書きますか?
Pythonです。Flet 1.0.4はPython 3.10以上で動きます。HTML・CSS・JavaScriptやDartは書きません。
Fletの最新バージョンはいくつですか?
2026年10月18日時点の安定版は1.0.4(2026年10月8日公開)です。初の安定版1.0.0は2026年9月15日に発表されました。手元の版は flet --version で確認できます。
古いFletのコード(ft.app)が動かないのはなぜですか?
1.0で ft.app() が削除され、起動は ft.run(main) に変わったためです。あわせて ft.ElevatedButton は ft.Button に、ft.colors は ft.Colors に変わり、ft.UserControl は削除されました。
Fletでアプリを終了するにはどうしますか?
デスクトップアプリでは、非同期のハンドラーから await page.window.close() を呼びます。終了前に確認を出すなら page.window.prevent_close = True にして page.window.on_event で閉じる要求を受け取り、確認後に await page.window.destroy() で閉じます。開発中はターミナルで Ctrl+C を押しても止まります。