Fletのアプリをfly.ioへデプロイしてcloudflare workers&D1と連携

前回、Fletの機能を実装を交えて、確認しました。今回はその続きです。
1. [Flet使うとPythonでWeb/Desktop/Mobileアプリ作れちゃうんだ](https://qiita.com/eno49conan/items/b34e435453148a9f2323)　←前回の内容
2. **Flet Docs (controls編) + cloudflareのWorkersデプロイ(D1を添えて)** ←今回ここです！
3. 1と2の学習を経て、実践的なサイト構築予定（詳細未定です）

Fletの以下ドキュメントで気になる内容に対して、手を動かしてみました。しかし、レイアウトだけでは・・・ってところです。今回もcloudflareと連携させます。workersとD1との連携を含めた形で進めようと思いました。
どちらかというと、今回はcloudflareの割合が高いと思います！！

https://flet.dev/docs/controls

# cloudflare WorkersとD1
前回の記事ではcloudflareのpagesを用いて、Fletで作成した簡単なアプリケーションのデプロイまで行いました。冒頭の話と重なりますが、今回はAPI(Workers)とデータベース(D1)に登場していただきます。

:::note
今回のゴールは、**Workers**(_Build serverless applications_)と**D1**(_serverless SQL databases_)の連携方法を理解し、Fletで作成した画面上でそれらを利用できるようにすることです。
:::

https://developers.cloudflare.com/workers/

https://developers.cloudflare.com/d1/

**※最初にD1のページに記載された注意書きについて共有します。**

:::note warn
> Open Alpha While in the Open Alpha period, there is a possibility of breaking changes. The Alpha is meant for testing purposes and using it for production traffic is not recommended.

D1は現在オープン アルファ版であり、本番データおよびトラフィックには推奨されません。
:::

### 実装準備
以下ページの手順通りで、デプロイまで完了でいます。

https://developers.cloudflare.com/d1/get-started/

:::note
コマンド自体を覚える必要は特にないので、都度コピペすればいいです。
今回は内容を整理するために、**各コマンド実行後に、ローカル環境とクラウド環境上でそれぞれどういった状態になっているのか**、図を載せることにしました。
:::

**Workersのプロジェクト作成**
```
wrangler init d1-tutorial -y
```
ローカル環境（PCのイラスト）にWorkersの土台を作成します。Workersの画像を少し薄くしていますが、ローカル≒ダミー、という私の感覚でこのようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/be69e2fe-1685-2dfe-4ceb-7e83e377b522.png)
**DB作成**
```
wrangler d1 create <DATABASE_NAME>
```
リモート環境に、データベースだけを作成します。（テーブルなどはないです）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/b5b16a41-c804-0eca-db98-a92bdc1b7358.png)
この段階でフォルダは以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/c745546f-71b0-0c2e-1484-ef3c3113c917.png)
**スキーマ定義と初期データ投入**
プロジェクトのルートディレクトリに`schema.sql`を作成して、必要なテーブルの作成や初期データ投入などを実施します。`--local`をつけると、ローカル環境だけにスキーマを適用します。`schema.sql`の内容は、チュートリアル内に記載されている内容をそのまま記載してます。
```
npx wrangler d1 execute sample-d1 --local --file=./schema.sql
```
データが投入されたか確認するために、コマンドパレットを開きます。（VsCodeで作業してます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/0c2ff577-c5b8-f724-e4dc-dc721d6d5265.png)
選択肢に出てきますので、選択します。（直前に記載したコマンドを実行で、自動的に生成されます。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/5864d219-8724-7962-0d70-3cad12044863.png)
`Show Table`をクリックすれば、データを確認できます。（初期データは4件あります）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/1c8e80df-3548-8c07-27ee-6d668e3744fc.png)
ここまでのイメージが以下の通りです。これでローカル環境で動作確認できる状態が整いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/046a43b2-02e7-5105-dd0a-49b9e4b227e8.png)

### D1構築時のファイル確認
**wrangler.tomlの修正**
`fly.io`を使うようになってから`toml`ファイルを触るようになりましたが、`XXX.ini`や`XXX.yaml`などの拡張子でもある設定ファイルのようなもの、という認識で作業してます。
```toml:wrangler.toml
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "<DATABASE_NAME>"
database_id = "<unique-ID-for-your-database>"
```
`database_id`はcloudflareのマネジメントコンソールから取得します。
以下画像の`<unique-ID-for-your-database>`部分にIDが記載されてます。コピーして貼り付けます。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/b8ce442a-a8ca-d396-0693-417066e657f1.png)
**`src/index.ts`を確認**
特定のPathへのアクセス時はXXXを、それ以外のPathの場合はYYYをレスポンスに設定する構造になってます。
    
実装以外の話ですが、**Workers以外の何と連携できるのか**わかるコメントは助かります。公式ドキュメントを辿れば分かりますが、試しに触ってみた段階で分かるのはうれしいです。
```typescript:index.ts
    export interface Env {
    	// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
    	// MY_KV_NAMESPACE: KVNamespace;
    	//
    	// Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
    	// MY_DURABLE_OBJECT: DurableObjectNamespace;
    	//
    	// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
    	// MY_BUCKET: R2Bucket;
    	//
    	// Example binding to a Service. Learn more at https://developers.cloudflare.com/workers/runtime-apis/service-bindings/
    	// MY_SERVICE: Fetcher;
    	MY_SAMPLE_DB: D1Database;
    }
```

# 実装開始
以下の機能があると、WorkersとD1を利用したアプリケーションかな～といったところです。
- ルートパスは読み込み時にデータ取得（GETリクエスト）を行い、全件表示
- データ登録のためのフォームを用意して、POSTリクエスト
- データ登録時、必要項目に値が設定されていない場合は、送信時ボタンを非活性
- DBへの接続に失敗した場合は、画面にメッセージ表示

### Layout検討
サンプルとしてTrolliが用意されていたので、それをベースにしました。

:::note
Trolliのサンプルは、今回実装予定のアプリケーションより機能が多いです。サンプルから不要と思う実装を削除する過程を挟んで作成を進めました。この作業方法の場合、以下画像のように何度もエラーが表示されます。それでも、このエラー過程を挟むことで、実装に対する理解が深めることができるので、やってよかったと思いました。

**何かエラーがある場合の画面表示形式**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/cd2f6cfd-b266-82c7-41f8-f9e538861407.png)
:::

### PythonでPostリクエストの実装
Fletのアプリケーションからリクエストを投げるための実装方法確認です。
Pythonのrequestsを用いたPOST通信の実装方法（Bobyの設定）

https://zero-cheese.com/9053/

### CloudflareのWorkersでD1からデータ取得・操作
こちらは、WorkersからD1に対する指示をする方法を確認しました。

https://developers.cloudflare.com/d1/tutorials/build-a-comments-api/#insert-data

※[Honoを使ったルーティング定義というものありました。](https://developers.cloudflare.com/d1/tutorials/build-a-comments-api/)

### ローカル開発

:::note
workersとD1がローカルだけで完全再現できることが、本当に開発を行いやすくしてくれます。
Fletのコードをローカルで実装後、ローカルで挙動を確認して、問題がなさそう！となったら、デプロイすればOKです。
:::

以下コマンドでWorkersをローカル環境で起動します。(`wrangler`2.19.0`の場合)
```
npx wrangler dev --local --persist
```
真ん中あたりの枠で囲まれた部分にある通り、キーボードの入力値によって、挙動が変わるようです。動かすだけであれば、`[b] open a browser`になるかと。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/6dc8152f-b6b8-3ae6-46b0-6b85070ffdab.png)
`127.0.0.1`なので、自身のPC（ローカル）です！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/4f3eb439-5772-118a-3297-a82625f10cb0.png)
**wrangler `3.1.1`による動作確認**
※wrangler `2.19.0`使用時に、wrangler `3.1.1`の使用を推奨されたためバージョンを変えてみました。以下コマンドでローカルDBの起動ができました。
```
npx wrangler dev --local
```
また、コンソールを見ると・・・
```
▲ [WARNING] --local is no longer required and will be removed in a future version.
```
`--local`はもういらない、とのことです。以下コマンドで起動できました。
```
npx wrangler dev
```
### 1日のリクエスト制限を大切に
実際のWorkersのURLに対してリクエストを実施すると、バンドルリクエスト数が加算されます。
`本日の`ということなので、10万リクエスト/日は、かなりの太っ腹な数ですが、節約できるならしたい、はずです！！**Workersをローカルで起動して、`http://127.0.0.1:8787`にアクセスする場合は、このバンドルリクエスト数がカウントされません。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/8a307755-c774-1a79-21db-6cfaaeb1c54a.png)

### 実装のワンシーン(Fletアプリからcloudflare Workersへの疎通確認)

画像の`insert_data_to_customer`をクリックで、処理が走ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/211d845a-af4b-a9a5-da6f-b443bc4c9454.png)

:::note
上記画像はあくまでも挙動確認のための簡単なアプリケーションです。Fletの挙動理解のためでもありますが、いきなり本格的に作り上げるとミスったときに、戻すのが大変でやる気が削がれるので・・・この方法をとりました。
:::

`Fletのアプリケーション`側のコードでPOSTリクエストを行うメソッド定義
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/2a515c42-ed1d-ec93-50fb-b3002c93ff80.png)
`cloudflare Workers`側では、Fletアプリケーション側からのリクエストのBodyが受け取れているか確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/ae38cc0c-26de-cedb-0343-a44803b79490.png)
**Flet側のコンソール**
200(HTTPレスポンス)なので、正常にリクエストができたことが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/11c916ae-f721-f3dc-acc3-21cdc391f954.png)
**cloudflare Workers側のコンソール**
データ受け取れてますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/d4ccc480-d35a-f5a0-90d9-1495d00aecaf.png)
### ローカルDBとリモートDB
ローカルで開発が完結していることを、データ数で確認してみました。以下画像はWorkersのコード側でデータ追加の実装が完成した直後のテーブルの状態です。（初期データが4件あったので、2件追加）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/46064ee6-92da-e3e9-85ab-93cb6560ec03.png)
このときに、リモートの状態（cloudflareのマネジメントコンソール）のデータ数を確認すると、4件で初期の状態から変わっていません。きちんと分かれてて、開発体験良しです～～
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/5d40e35e-6753-0fc1-a599-cb6e98f29394.png)

# 完成したアプリケーションの画面と実装
### 各画面と挙動
トップ画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/2e8e637b-09d6-3b94-f78a-0f6ff914d3a9.png)
ユーザ追加画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/3d76421a-7abd-938b-3ab4-dc4a8989eeb2.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/05fcebcb-aaeb-df44-405e-fc199aa40b16.png)
作成が成功したメッセージが表示されてから1秒経過すると、トップ画面（一覧画面）に戻る処理を入れています。一覧画面で、先ほどの入力値が追加されてますね!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/cefd14e3-9780-116c-c8a6-1dde8438767d.png)
**Workers側のサーバを起動しない・間違ったURLに対するリクエストを実行したときの画面**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/d0cfbc34-029a-68e5-3d86-4301aaaa60d9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/b4545e8d-2d1a-cc67-d697-4cffaf72f2a5.png)

### 実装を交えて（最後にリポジトリ情報を載せていますので、そちらを参照いただく形でもOK！です）
cloudflareの機能が素晴らしいことに感動して、Fletの話を忘れそうになります。

上記完成物の中で、以下に関する実装を取り上げます。
アクセスしたとき、以下の画面です。
`submit`を非活性にして、送信処理が実行されないようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/09665209-5e5b-dd7f-6b50-a546e23a1ec5.png)
`disabled=True`としています。この記述によって、画面表示時はボタンを非活性状態にできます。
```python
# 追加処理のボタン
submit_button = ft.ElevatedButton(
    disabled=True,
    text="Submit", on_click=button_clicked)
```
次に、テキストフィールドに値を入力したときの挙動を見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/811248e4-e37a-c148-f5f0-27352e731ebf.png)
各テキストフィールド(Company Name,Contract Name)それぞれに対して、`on_change`を定義しています。
テキストの内容が更新されると、`on_change=XXX`のXXXが実行されます。今回は`textfield_change`メソッドを定義しています。Company NameまたはContract Nameが空文字であれば、ボタンを非活性とする実装です。
両方の値が空文字でなければ、非活性状態が解除されます。
また`disabled`のステータスを更新するため、最後には、`self.page.update()`を実行します。このupdate処理は実装全体でも登場することがしばしばあります。
```python
# テキストフィールド内容更新時の処理
def textfield_change(e):
    if company_name_textfield.value == "" or contract_name_textfield.value == "":
        submit_button.disabled = True
    else:
        submit_button.disabled = False
    self.page.update()

# テキストフィールド
company_name_textfield = ft.TextField(
    label="Company Name", on_change=textfield_change)
contract_name_textfield = ft.TextField(
    label="Contract Name", on_change=textfield_change)
```
両方入力できていれば、`Submit`が活性状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/4c7520c5-e4fa-1d09-51cf-ff45018df3fe.png)
送信しました。メッセージ表示がされたと同時に、テキストフィールドが空っぽにあり、ボタンも非活性状態に戻りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/3c906906-0f83-5acb-2243-43bb605c9070.png)
登録成功・失敗時のメッセージの要素について、`visible=False`としています。画面表示したタイミングでは非表示とするためですね。try-exceptまではPOSTリクエスト情報を作成しています。

リクエストを実行時には・・以下を満たす必要がありそうです。
- 登録**成功**
  - 登録できたことを伝える 
  - 画面をアクセスしたときの状態に戻す
- 登録**失敗**
  - 登録できなかったことを伝える

登録の結果を伝えるためのメッセージ表示については、`XXXX.visible = True`を使用します。
コードのコメントでも記載していますが、アクセスしたときの状態に戻すために、以下の処理が必要です。
- テキストフィールド初期化：`XXX.value = ""`
- ボタンを日活性に：`submit_button.disabled = True`

そして忘れずに`page.update()`を実行します。
```python
# customer登録失敗時のメッセージ表示
msg_failed_add_customer = Row(
    [
        ft.Text("Sorry Failed to send new customer, Retry Access few minutes Later", 
        size=24, color=ft.colors.RED_500
                )
    ],
    visible=False
)
# customer登録成功時のメッセージ表示
msg_success_add_customer = Row(
    [
        ft.Text("Success to create new customer!",
                size=24, color=ft.colors.BLUE_900,
                )
    ],
    visible=False
)

# 送信（Customer登録処理）
def button_clicked(e):
    if (len(company_name_textfield.value) == 0 or len(contract_name_textfield.value) == 0):
        page.update()
        return
    # POSTリクエスト
    url = os.getenv('WORKERS_URL')
    data = {
        'CompanyName': company_name_textfield.value,
        'ContactName': contract_name_textfield.value,
    }
    data_encode = json.dumps(data)
    try:
        response = requests.post(url, data=data_encode)
        # テキストフィールド初期化
        company_name_textfield.value = ""
        contract_name_textfield.value = ""
        # 画面の状態更新
        submit_button.disabled = True
        msg_failed_add_customer.visible = False
        msg_success_add_customer.visible = True
        page.update()
    except:
        msg_failed_add_customer.visible = True
        page.update()

# 追加処理のボタン（再掲）
submit_button = ft.ElevatedButton(
    disabled=True,
    text="Submit", on_click=button_clicked)
```

最後は余談ですが、FletがそもそもFlutterを意識されているので、Rowの実装を見ていると、Flutterに近いものがあると感じました。
<details><summary>今回のFletの実装</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/f900de2c-6390-9ba8-77aa-3433b338e4c0.png)
</details>
<details><summary>Flutterの実装（過去の実装からテキトーに取り出しました）</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/988f0ba3-a34a-edd2-a546-cab83be9e4a3.png)
</details>

# WorkersとD1のデプロイ
**テーブル定義のデプロイ**
これまでローカルだけで定義していたスキーマを、リモートに対して適用します。
これまでローカルで使用していたテーブルのデータは影響を受けず、`schema.sql`に記述した定義を今度はリモート環境に適用します。
```
npx wrangler d1 execute sample-d1 --file=./schema.sql
```
cloudflare上にD1にテーブルが作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/840c45d6-b5ea-b268-9eed-56dc6b4d3054.png)

**Workersの処理を記述したtsファイル自体のデプロイ**
GET/POSTリクエストを実装できたので、こちらもデプロイします。 
```
npx wrangler publish
```
コンソールの出力
```
Your worker has access to the following bindings:
- D1 Databases:
  - MY_SAMPLE_DB: {projectname} (b827dba5-47aa-427f-886e-d815287ca699)
Total Upload: 7.18 KiB / gzip: 2.27 KiB
Uploaded {projectname} (0.89 sec)
Published {projectname} (0.28 sec)
  https://{projectname}.{github-username}.workers.dev
Current Deployment ID: 7bd71725-ac94-401c-bd37-274b697b83f7
```
cloudflare上にWorkerが構築されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/ee439204-3c02-f19e-680e-5dbe001fd223.png)

**fletのアプリケーションをpages上にデプロイ(githubのリポジトリ経由)**
cloudflare上にpagesが構築されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/dbbcc747-6611-2717-0d89-f67e1933a488.png)

**よし最終確認だ！と思って、デプロイしたアプリケーションの動作確認を始めると・・・**
```
Can't connect to HTTPS URL because the SSL module is not available.
```

https://stackoverflow.com/questions/72585785/caused-by-sslerrorcant-connect-to-https-url-because-the-ssl-module-is-not-av

読んでみると、どうやら・・・`Pyodide`が関係している？？？

:::note alert

前回の記事で`cloudflare pages`にデプロイしたアプリケーションは大した機能がないので、何も起きませんでした。ですが今回のように、いくつか機能を加えてライブラリも追加するようになると、不具合が発生するケースもあることが確認できました。
Fletの公式ドキュメントにも、以下のように`Flet static cons`として以下の記述があります。

> Limited Python compatibility - not every program that works with native Python can be run with Pyodide.

`Pyodide`で動かした場合に、Pythonで動いても機能しない部分がある、といった訳でしょうか。

:::

https://flet.dev/docs/guides/python/publishing-static-website

ということで・・・
**fletのアプリケーションを~~cloudflare pages~~`fly.io`上にデプロイ**に変更します！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/b067acdd-6bc5-3255-355b-83b5be3e61f4.png)
公式ドキュメントに沿って、進めます。

https://flet.dev/docs/guides/python/deploying-web-app/hosting-providers/fly-io

`fly launch`コマンド実行
自動生成される`Procfile`というファイルがあります。そこに、起動時のコマンドを設定します。
デフォルトは以下のようになっていました。（1行目はコメントです。）
```
# Modify this Procfile to fit your needs
web: gunicorn server:app
```
今回は、通常にPythonコマンドで動作確認もしていたので、それを使います。
```
# Modify this Procfile to fit your needs
web: python main.py
```

`fly deploy`コマンドでデプロイして、画面から確認してみます。
きちんとデータが表示できました・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/97e97825-f754-1b01-f7fc-c3d000a24de3.png)


# Fletのデザインについて学習した
cloudflareとそのためのアプリケーションの方が内容盛りだくさんで、かつ私自身としても共有したい内容だったので、Fletの内容確認は後ろに回しました。
前回と同様ですが全ては触っていません。実装を重ねる中で適宜調べるので、ざっくり見て気になった項目を確認しました。詳しい情報は公式ドキュメントをみていただきたいと思います。
また公式からデモページが用意されているので、こちらで動作確認がすぐに行えます。

https://flet-controls-gallery.fly.dev/dialogs

### [Appbar](https://flet.dev/docs/controls/appbar)
参考にしている、このドキュメントにもおそらく使われているパーツだと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/35d94f49-19b1-7441-0cdb-d5cd190e55c1.png)
Appbarが画面上部ですが、横・下部に同様の実装を、navigationrail,navigationbarで説明していると思います。（省略）

### [ResponsiveRow](https://flet.dev/docs/controls/responsiverow)
`Responsive`という単語で想像つく部分はありましたが、実装方法を見てみました。
bootstrapやtailwindcsを経験された方ですと、レイアウトの話で`12`という数字はなじみがあるかと思います。画面幅がどれくらいであれば、どのように表示する、といった制御を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/23318b39-96db-f46f-62a1-958a4732043c.png)
### [Image](https://flet.dev/docs/controls/image)
画像が表示できないのは困るので調べました。CORSの問題があったりして、画像が表示できないケースもある。
[サンプルコード](https://flet.dev/docs/controls/image/)にあった`https://picsum.photos/200/200`の画像を表示する場合に、ブラウザで起動するとCORSエラーになるので、ft.appの引数に`web_renderer="html"`を設定したりして、対応する必要もありそうです。

https://flet.dev/docs/controls/text/#using-system-fonts

### [progressbar](https://flet.dev/docs/controls/progressbar)
> **メモ**：処理に時間を要して、ユーザに待ってもらうときに、使うと思った。
これはFletに限った話ではないが、N秒でプログレスの最後まで到達するのか制御できるので、使いやすい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/81287d25-cc19-d002-8b48-5d3a884b668c.png)

### [IconButton](https://flet.dev/docs/controls/iconbutton)
> **メモ**：各商品ページやアカウントページなどでは、使いそうかな～

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/9a0fc597-6e70-f14f-d5ae-f7eeec52dfbf.png)
### [AlertDialog](https://flet.dev/docs/controls/alertdialog)
> **メモ**：ボタンをクリックしたとき、何か警告などを表示するときに使いそうかな～、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/887a05b3-3799-8a4e-825c-86a26ccdf7e5.png)

### [GestureDetector](https://flet.dev/docs/controls/gesturedetector)
Flutterでもお世話になる機会が多かったので、こちらでも触れておきます。
何か画面でイベント（入力、ボタンのクリック）が発生したときに、どんな処理を

### [Footer](https://github.com/flet-dev/examples/blob/main/python/apps/layouts/footer.py)
アプリケーションでフッターはよく見ますが、そのサンプルをみてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/05d63145-57a4-4152-29be-00e1914431e3.png)

### 実装について

https://github.com/eno-conan/Flet_cloudflare-workers-d1-sample

# 今後
Fletの基礎部分と、cloudflareのWorkdersとD1の基礎について、理解することができました。
次回以降は少し作成まで間が空いてしまうかもしれませんが、もう少し実践的な内容に挑戦してみようと考えています。