[Flet入門] Text・TextField・TextButtonで作る基本的なUI要素の実装方法

## はじめに

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/6921d8ff-b07d-49ba-82af-b15d64633e6f.png)

Fletは、Pythonを使ってクロスプラットフォームのUIアプリケーションを簡単に構築できるフレームワークです。Flutterをバックエンドに使用しているため、美しいマテリアルデザインのUIを持つアプリケーションを作成できます。

Fletの特徴は以下の通りです：

- **Pythonだけで完結**: フロントエンドの知識（HTML、CSS、JavaScriptなど）が不要
- **クロスプラットフォーム**: 同じコードでウェブ、デスクトップ、モバイルに対応
- **シンプルなAPI**: 少ないコードで機能的なUIを構築可能
- **リアクティブ**: データの変更に応じてUIが自動的に更新される

この記事では、**Fletの基本的なテキスト系UI要素の使い方**を紹介します。

## 環境構築

まずはFletをインストールしましょう。

```bash
pip install flet
```

## 基本的なテキスト系UI要素

Fletでは、様々なテキスト系UI要素が用意されています。ここでは、以下の4つを紹介します：

1. Text: 基本的なテキスト表示
2. TextField: テキスト入力フィールド
3. TextButton: テキストボタン
4. 様々なテキストスタイリング

### 実装例

以下のコードは、これらのUI要素を組み合わせた完全なサンプルアプリケーションです。

```python
import flet as ft

def main(page: ft.Page):
    # ページの設定
    page.title = "Flet テキスト系UI要素のサンプル"
    page.theme_mode = ft.ThemeMode.LIGHT
    page.padding = 20
    
    # 長いテキストコンテンツを持つColumn作成（スクロール表示用）
    scroll_content = ft.Column(spacing=10)
    
    # 複数のテキスト要素を追加
    for i in range(1, 31):
        scroll_content.controls.append(
            ft.Text(f"テキスト行 {i}: サンプルテキストです。これはスクロールのテスト用です。", size=16)
        )
    
    # スクロール可能なコンテナ - スタイル適用のためにContainerでラップ
    scrollable_section = ft.Column([
        ft.Text("スクロールバーのサンプル", size=20, weight=ft.FontWeight.BOLD),
        ft.Container(
            content=ft.Column([
                # ListViewではなく、Columnとして配置
                ft.Container(
                    content=scroll_content,
                    height=300,  # 高さを制限
                    padding=20,
                    bgcolor=ft.Colors.BLUE_50,
                    border_radius=10,
                ),
            ]),
            bgcolor=ft.Colors.WHITE,
            border=ft.border.all(1, ft.Colors.GREY_400),
            border_radius=10,
            padding=10,
            margin=ft.margin.only(bottom=20),
        )
    ])
    
    # 1. 基本的なテキスト (Text)
    text_section = ft.Container(
        content=ft.Column(
            [
                ft.Text("1. 基本的なテキスト (Text)", size=20, weight=ft.FontWeight.BOLD),
                ft.Text("通常のテキスト表示です。"),
                ft.Text("サイズを変更したテキスト", size=25),
                ft.Text("太字のテキスト", weight=ft.FontWeight.BOLD),
                ft.Text("イタリック体のテキスト", italic=True),
                ft.Text("カラーを変更したテキスト", color=ft.Colors.RED),
                ft.Text("背景色を持つテキスト", bgcolor=ft.Colors.YELLOW_100),
                ft.Text("中央揃えのテキスト", text_align=ft.TextAlign.CENTER, width=400),
                ft.Text(
                    "長いテキストは自動的に折り返されます。" * 5,
                    width=400,
                ),
            ],
            spacing=10,
        ),
        padding=20,
        bgcolor=ft.Colors.BLUE_50,
        border_radius=10,
        margin=ft.margin.only(bottom=20),
    )
    
    # 2. テキストフィールド (TextField)
    def on_text_change(e):
        text_output.value = f"入力値: {text_field.value}"
        page.update()
    
    text_field = ft.TextField(
        label="名前を入力してください",
        hint_text="ここに入力",
        prefix_icon=ft.Icons.PERSON,
        helper_text="あなたのお名前を入力してください",
        on_change=on_text_change,
    )
    
    password_field = ft.TextField(
        label="パスワード",
        password=True,
        can_reveal_password=True,
    )
    
    multiline_field = ft.TextField(
        label="複数行のテキスト",
        multiline=True,
        min_lines=3,
        max_lines=5,
    )
    
    text_output = ft.Text("入力値: ")
    
    textfield_section = ft.Container(
        content=ft.Column(
            [
                ft.Text("2. テキストフィールド (TextField)", size=20, weight=ft.FontWeight.BOLD),
                text_field,
                password_field,
                multiline_field,
                text_output,
            ],
            spacing=20,
        ),
        padding=20,
        bgcolor=ft.Colors.GREEN_50,
        border_radius=10,
        margin=ft.margin.only(bottom=20),
    )
    
    # 3. テキストボタン (TextButton)
    def on_button_click(e):
        button_output.value = f"クリックされたボタン: {e.control.text}"
        page.update()
    
    simple_button = ft.TextButton("シンプルなテキストボタン", on_click=on_button_click)
    
    # スタイル付きボタン
    styled_button = ft.TextButton(
        "スタイル付きボタン",
        icon=ft.Icons.THUMB_UP,
        on_click=on_button_click,
        style=ft.ButtonStyle(
            color=ft.Colors.BLACK,
            bgcolor=ft.Colors.BLUE_100,
        ),
    )
    
    # ホバー効果
    hover_button = ft.ElevatedButton(
        "ホバー効果付きボタン",
        icon=ft.Icons.THUMB_UP,
        on_click=on_button_click,
    )
    
    disabled_button = ft.TextButton("無効化されたボタン", disabled=True)
    
    button_output = ft.Text("ボタンをクリックしてください")
    
    button_section = ft.Container(
        content=ft.Column(
            [
                ft.Text("3. テキストボタン (TextButton)", size=20, weight=ft.FontWeight.BOLD),
                ft.Row([simple_button, styled_button, hover_button, disabled_button], spacing=10),
                button_output,
            ],
            spacing=20,
        ),
        padding=20,
        bgcolor=ft.Colors.PURPLE_50,
        border_radius=10,
        margin=ft.margin.only(bottom=20),
    )
    
    # 4. さまざまなテキストスタイリング
    styling_section = ft.Container(
        content=ft.Column(
            [
                ft.Text("4. さまざまなテキストスタイリング", size=20, weight=ft.FontWeight.BOLD),
                ft.Text("フォントファミリー", font_family="Courier New"),
                
                # 下線付きテキスト - TextSpanとTextStyleを使用
                ft.Text(
                    spans=[
                        ft.TextSpan(
                            "テキストの装飾（下線）",
                            ft.TextStyle(decoration=ft.TextDecoration.UNDERLINE)
                        )
                    ]
                ),
                
                # 取り消し線 - TextSpanとTextStyleを使用
                ft.Text(
                    spans=[
                        ft.TextSpan(
                            "取り消し線",
                            ft.TextStyle(decoration=ft.TextDecoration.LINE_THROUGH)
                        )
                    ]
                ),
                
                # 文字間隔の調整 - TextSpanとTextStyleを使用
                ft.Text(
                    spans=[
                        ft.TextSpan(
                            "文字間隔の調整",
                            ft.TextStyle(letter_spacing=5)
                        )
                    ]
                ),
                
                # 背景と境界線 - Containerを使用
                ft.Container(
                    content=ft.Text("背景と境界線"),
                    bgcolor=ft.Colors.AMBER_100,
                    border=ft.border.all(1, ft.Colors.AMBER),
                    padding=10,
                ),
                
                # 影付きテキスト - Containerを使用
                ft.Container(
                    content=ft.Text("影付きテキスト"),
                    shadow=ft.BoxShadow(
                        blur_radius=2, 
                        color=ft.Colors.BLACK54
                    ),
                    padding=10,
                ),
                
                ft.Text(
                    "複数のスタイルを持つテキスト",
                    spans=[
                        ft.TextSpan(
                            "赤色のテキスト",
                            ft.TextStyle(color=ft.Colors.RED, italic=True),
                        ),
                        ft.TextSpan(
                            "青色のテキスト",
                            ft.TextStyle(color=ft.Colors.BLUE, weight=ft.FontWeight.BOLD),
                        ),
                    ],
                ),
                ft.SelectionArea(
                    content=ft.Text("このテキストは選択可能です。コピー＆ペーストできます。"),
                ),
            ],
            spacing=10,
        ),
        padding=20,
        bgcolor=ft.Colors.ORANGE_50,
        border_radius=10,
    )
    
    # ページ全体のコンテンツ
    page_content = ft.Column([
        ft.Text("Fletの基本的なテキスト系UI要素", size=30, weight=ft.FontWeight.BOLD),
        scrollable_section,
        text_section,
        textfield_section,
        button_section,
        styling_section,
    ])
    
    # ページ全体をスクロール可能に
    page.scroll = "auto"
    page.add(page_content)


ft.app(target=main)
```

## 各UI要素の詳細説明

### 1. Text（基本的なテキスト）

`Text`ウィジェットは最も基本的なテキスト表示コンポーネントです。

```python
# 基本的な使い方
ft.Text("通常のテキスト表示です。")

# サイズ変更
ft.Text("サイズを変更したテキスト", size=25)

# スタイリング
ft.Text("太字のテキスト", weight=ft.FontWeight.BOLD)
ft.Text("イタリック体のテキスト", italic=True)
ft.Text("カラーを変更したテキスト", color=ft.Colors.RED)
```

### 2. TextField（テキストフィールド）

`TextField`は、ユーザーからのテキスト入力を受け付けるコンポーネントです。

```python
# 基本的な使い方
ft.TextField(
    label="名前を入力してください",
    hint_text="ここに入力",
    prefix_icon=ft.Icons.PERSON,
    helper_text="あなたのお名前を入力してください",
    on_change=on_text_change,  # コールバック関数
)

# パスワード入力
ft.TextField(
    label="パスワード",
    password=True,
    can_reveal_password=True,
)

# 複数行入力
ft.TextField(
    label="複数行のテキスト",
    multiline=True,
    min_lines=3,
    max_lines=5,
)
```

### 3. TextButton（テキストボタン）

`TextButton`は、クリック可能なテキストベースのボタンです。

```python
# 基本的な使い方
ft.TextButton("シンプルなテキストボタン", on_click=on_button_click)

# スタイル付きボタン
ft.TextButton(
    "スタイル付きボタン",
    icon=ft.Icons.THUMB_UP,
    on_click=on_button_click,
    style=ft.ButtonStyle(
        color=ft.Colors.BLACK,
        bgcolor=ft.Colors.BLUE_100,
    ),
)

# 無効化されたボタン
ft.TextButton("無効化されたボタン", disabled=True)
```

### 4. 様々なテキストスタイリング

Fletでは、テキストに対して様々なスタイリングが可能です。

```python
# フォントファミリーの変更
ft.Text("フォントファミリー", font_family="Courier New")

# テキストの装飾 - TextSpanとTextStyleを使用
ft.Text(
    spans=[
        ft.TextSpan(
            "テキストの装飾（下線）",
            ft.TextStyle(decoration=ft.TextDecoration.UNDERLINE)
        )
    ]
)

# 取り消し線 - TextSpanとTextStyleを使用
ft.Text(
    spans=[
        ft.TextSpan(
            "取り消し線",
            ft.TextStyle(decoration=ft.TextDecoration.LINE_THROUGH)
        )
    ]
)

# 文字間隔の調整 - TextSpanとTextStyleを使用
ft.Text(
    spans=[
        ft.TextSpan(
            "文字間隔の調整",
            ft.TextStyle(letter_spacing=5)
        )
    ]
)

# 複数のスタイルを持つテキスト
ft.Text(
    "複数のスタイルを持つテキスト",
    spans=[
        ft.TextSpan(
            "赤色のテキスト",
            ft.TextStyle(color=ft.Colors.RED, italic=True),
        ),
        ft.TextSpan(
            "青色のテキスト",
            ft.TextStyle(color=ft.Colors.BLUE, weight=ft.FontWeight.BOLD),
        ),
    ],
)
```

## スクロール機能の実装

Fletでスクロール機能を実装するには、コンテナの高さを制限し、ページのスクロールプロパティを設定します。

```python
# コンテナの高さを制限して、コンテンツがオーバーフローするようにする
container = ft.Container(
    content=content,
    height=300,  # 高さを制限
)

# ページ全体をスクロール可能に設定
page.scroll = "auto"  # または ft.ScrollMode.AUTO
```

スクロールモードには以下のオプションがあります：

- `AUTO`：必要な時だけスクロールバーを表示
- `ALWAYS`：常にスクロールバーを表示
- `HIDDEN`：スクロールは可能だがスクロールバーは非表示
- `NONE`（デフォルト）：スクロール不可

## 注意点：Fletのバージョンによる違い

Fletは活発に開発が進められているフレームワークであり、バージョンによってAPIが変更されることがあります。このコードはFlet 0.25.0以降を前提としていますが、以下の変更点に注意してください：

- `ft.colors` → `ft.Colors`への変更
- テキスト装飾（下線、取り消し線など）は直接設定できず、`TextStyle`を通して設定する必要がある
- `ListView`のプロパティと使用方法の変更

最新のAPIについては、[公式ドキュメント](https://flet.dev/docs/)を参照してください。

## 実行結果

このコードを実行すると、以下のようなアプリケーションが表示されます：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/8010716b-44fe-4358-a887-dddb82f7dfc0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/0defbd41-7e5f-43ba-81d9-2994ca5a5d9c.png)

## まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/87c60acf-3cb5-42a6-90d8-67fda73a5d89.png)

Fletは、Pythonだけで美しいUIを持つクロスプラットフォームアプリケーションを簡単に構築できるフレームワークです。この記事では、基本的なテキスト系UI要素の使い方を紹介しました。

Fletの主な利点は以下の通りです：

- **シンプルなAPI**: 少ないコードでリッチなUIを作成できます
- **クロスプラットフォーム**: 同じコードで複数のプラットフォームに対応できます
- **Pythonだけで完結**: フロントエンドの知識（HTML、CSS、JavaScript）が不要です
- **リアクティブ**: データの変更に応じて自動的にUIを更新します

Fletはまだ比較的新しいフレームワークですが、シンプルさと強力な機能を兼ね備えており、今後さらに発展していくことが期待されます。

## 参考リンク

- [Flet公式サイト](https://flet.dev/)
- [Flet GitHub リポジトリ](https://github.com/flet-dev/flet)
- [Flet公式ドキュメント](https://flet.dev/docs/)