Fletを試す(1) - PythonでFlutterアプリが作れるフレームワーク

# はじめに

PythonでWebアプリ、デスクトップアプリ、モバイルアプリを作れるフレームワーク「Flet」の情報が流れてきて、興味を持ったので試してみることにした。

公式サイトはこちら。
https://flet.dev/

詳しく解説してくれている記事はこちら。
https://qiita.com/NasuPanda/items/48849d7f925784d6b6a0

「Flet」は以下の点で良さそうに思いました。
- Pythonだけで全部作れる
- HTMLもJavascriptも不要
- アプリの見た目も良い
- 中身はFlutter
- 1ソースで、マルチOS、マルチ端末に対応できる
- 「Fly.io」でアプリの公開も可能

# 前提事項

- 「Flet」に関する詳細は省略
- インストール方法、実行方法、デプロイ方法なども省略
- 試した環境
    - Windows10
    - Python 3.10.8
    - Flet 0.3.2

# 概要

公式サイトなどを参考に以下をやってみた。
- Fletで簡単な画面レイアウトを作成してみる
- 全部は無理なので主に使いそうな部品だけを配置
- コーディング、デバッグ、デプロイまで試す

最終形を「Fly.io」にデプロイしたものが以下。

https://flet-report01.fly.dev/#/
![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273897/6e5a2501-ff70-dd71-7f45-ae26593fdebe.gif)

個人的な感想は以下。

- GUIでレイアウトは作れないがVBのような感じ
- デフォルトでも見た目は良い
- 日本語も大丈夫
- 付属のコマンド「flet xx.py -d」で実行しながらコーディングできてデバッグしやすい
- HTMLとJavascriptなしでここまで作れるのは凄い
- レイアウト部品の使い分けがいまいちよくわからなかった（Row、Colmun、Card、Containerとか）
- カレンダーとかもう少し部品が必要かなと思った

# 詳細

## 基本のソース構成

```python
import flet as ft

def main(page: ft.Page):
    t = ft.Text(value="Hello, world!")
    page.add(t)

ft.app(target=main)
```

↑が「Hello, world!」と表示する基本のソース。
「ft.Text」がテキスト表示の部品。FletではControlと呼ぶ。
画面全体がPageで、そこに部品を追加するイメージで画面レイアウトを作る。

実行結果は↓。 ※WinAPP
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273897/56731eb0-b5b1-595c-9ff9-d89854607654.png)

## Control（画面部品）のメモ

主要なControlのメモを以下にまとめる。
表示した様子は概要を参照。

### Text

```python
    t = ft.Text(
        value="Hello, world!",
    )
```
テキスト表示のときに使う。

### TextField

```python
    tf = ft.TextField(
        label="ラベル", value="Hello, world!",
    )
```
テキスト入力のときに使う。

### Checkbox

```python
    cb = ft.Checkbox(
        label="ラベル", value=False,
    )
```
選択をするときに使う。
あまり見ないけど「未定」という3つ目の状態も持てる。

### Dropdown

```python
    dd = ft.Dropdown(
        label="ラベル",
        width=200,
        options=[
            ft.dropdown.Option("Red"),
            ft.dropdown.Option("Green"),
            ft.dropdown.Option("Blue"),
        ],
    )
```
リストから選択するときに使う。

### RadioGroup

```python
    rg = ft.RadioGroup(
        value="green",
        content=ft.Row([
            ft.Radio(value="red", label="Red"),
            ft.Radio(value="green", label="Green"),
            ft.Radio(value="blue", label="Blue"),
        ]),  # Row を Column にすると縦になる
    )
```
リストではなくラジオボタンで選択するときに使う。
選択肢が少ないならこっち。

### Switch

```python
    sw = ft.Switch(
        label="ラベル", value=True,
    )
```
チェックボックスと同様。
見た目が好きならこちら。

### Slider

```python
    s = ft.Slider(
        label="{value}", value=50,
        min=0, max=100, divisions=10,
    )
```
数値を調整するときに使える。
あまり使わないかも。

### ElevatedButton

```python
    eb1 = ft.ElevatedButton("登録", icon=ft.icons.ADD)
```
ボタンの一種。
普通のボタンはこれ。アイコンを付けれる。

### IconButton

```python
    ib1 = ft.IconButton(icon=ft.icons.ADD)
```
ボタンの一種。
アイコンのみのボタンはこれ。

## ソース最終形

上記のControlを全て配置したソースがこちら。
表示は概要に貼っているイメージになる。

```python
import flet as ft


def main(page: ft.Page):

    # タイトル設定
    page.title = "Sample App"

    # Text を作成
    t = ft.Text(
        value="Hello, world!",
    )

    # TextField を作成
    tf = ft.TextField(
        label="ラベル", value="Hello, world!",
    )

    # Checkbox を作成
    cb = ft.Checkbox(
        label="ラベル", value=False,
    )

    # Dropdown を作成
    dd = ft.Dropdown(
        label="ラベル",
        width=200,
        options=[
            ft.dropdown.Option("Red"),
            ft.dropdown.Option("Green"),
            ft.dropdown.Option("Blue"),
        ],
    )

    # RadioGroup を作成
    rg = ft.RadioGroup(
        value="green",
        content=ft.Row([
            ft.Radio(value="red", label="Red"),
            ft.Radio(value="green", label="Green"),
            ft.Radio(value="blue", label="Blue"),
        ]),  # Row を Column にすると縦になる
    )

    # Switch を作成
    sw = ft.Switch(
        label="ラベル", value=True,
    )

    # Slider を作成
    s = ft.Slider(
        label="{value}", value=50,
        min=0, max=100, divisions=10,
    )

    # ElevatedButton を作成
    eb1 = ft.ElevatedButton("登録", icon=ft.icons.ADD)
    eb2 = ft.ElevatedButton("更新", icon=ft.icons.UPDATE)
    eb3 = ft.ElevatedButton("削除", icon=ft.icons.DELETE, disabled=True)
    eb = ft.Row(controls=[eb1, eb2, eb3])

    # IconButton を作成
    ib1 = ft.IconButton(icon=ft.icons.ADD)
    ib2 = ft.IconButton(icon=ft.icons.REFRESH)
    ib3 = ft.IconButton(icon=ft.icons.DELETE_OUTLINED, disabled=True)
    ib = ft.Row(controls=[ib1, ib2, ib3])

    # Page に追加
    page.add(
        ft.Card(
            content=ft.Container(
                content=ft.Column(
                    controls=[
                        t, tf, cb, dd, rg, sw, s,
                        eb, ib
                    ], spacing=20,
                ), padding=20
            ), margin=20
        )
    )
    # page.add(t) は↓と同じなので add を使えばよい
    # page.controls.append(t)
    # page.update()


ft.app(target=main)
```

以上です。