TextFieldのPlaceholderを表示させたい

Android Jetpack Composeでテキスト入力を受け付けるコンポーネントとしてTextFieldをよく使用します。そんな中でもよく使う機能であるPlaceholder(プレースホルダー)を未入力の場合はずっと表示する方法を載せておきます。
※厳密にはプレースホルダーではないですが...
まずは普通にTextFieldでプレースホルダーを使用した際の動きを確認します。
# 通常
```kotlin
var textFieldValue by rememberSaveable { mutableStateOf("") }
TextField(
    value = textFieldValue,
    onValueChange = {
        textFieldValue = it
    },
    modifier = Modifier
        .wrapContentSize(),
    textStyle = Typography.bodyMedium,
    label = {
        Text(
            text = "グループ名",
            style = Typography.labelSmall
        )
    },
    placeholder = {
        Text(
            text = "グループ名を入力",
            style = Typography.bodyMedium
        )
    }
)
```
<img src="https://vegcale.com/home/wp-content/uploads/2024/07/textfield.gif"/>
<br>
プレースホルダーが表示されるのは、クリック後かつ未入力時となっています。
<br>
下記では、初期表示時、入力内容削除後のフォーカス外し時にもプレースホルダーを表示させる方法をご紹介します。

# 対応済みコード
```kotlin
var textFieldValue by rememberSaveable { mutableStateOf("グループ名を入力") }
TextField(
    value = textFieldValue,
    onValueChange = {
        textFieldValue = it
    },
    modifier = Modifier
        .wrapContentSize()
        .onFocusChanged {
             if (!it.isFocused) {
                textFieldValue = textFieldValue.ifEmpty { "グループ名を入力" }
            }
            
            if (!it.isFocused || textFieldValue != "グループ名を入力") return@onFocusChanged
            textFieldValue = ""
        }
        .defaultMinSize(
            minWidth = TextFieldDefaults.MinWidth,
            minHeight = TextFieldDefaults.MinHeight
        ),
    textStyle = Typography.bodyMedium,
    label = {
        Text(
            text = "グループ名",
            style = Typography.labelSmall
        )
    },
    placeholder = {
        Text(
            text = "グループ名を入力",
            style = Typography.bodyMedium
        )
    }
)
```
<img src="https://vegcale.com/home/wp-content/uploads/2024/07/textfield-result.gif"/>

# 詳細
## 初期表示時
クリック前の表示を実現するために、valueにプレースホルダーとして表示したい文字(下記では「グループ名を入力」)を設定しています。しかし、このままでは入力を開始した際に初期値が入力欄に存在してしまいます。そこでフォーカスされた際に現状の入力値が初期値と同じである場合にのみ入力欄をクリアしています。
```kotlin
var textFieldValue by rememberSaveable { mutableStateOf("グループ名を入力") }
TextField(
    value = textFieldValue,
    // 省略
    modifier = Modifier
        .wrapContentSize()
        .onFocusChanged {
            // 省略
            if (!it.isFocused || textFieldValue != "グループ名を入力") return@onFocusChanged
            textFieldValue = ""
        }
```
## 入力内容削除後のフォーカス外し時
上記のままでは何も入力していない状態で他のコンポーネントにフォーカスを移すと、プレースホルダーが表示されません。これを避けるため、フォーカスされている状態以外のタイミングで入力値が空であれば初期値を入力するように設定しています。
```kotlin
var textFieldValue by rememberSaveable { mutableStateOf("グループ名を入力") }
TextField(
    value = textFieldValue,
    // 省略
    modifier = Modifier
        .wrapContentSize()
        .onFocusChanged {
            if (!it.isFocused) {
                textFieldValue = textFieldValue.ifEmpty { "グループ名を入力" }
            }
            // 省略
        }
```
# おわりに
上記がプレースホルダーライクな表示を出し続ける方法です。問題点として、もしユーザーが初期値と全く同じ値(例の場合は「グループ名を入力」)を入力した場合に、入力し忘れか入力済みかの判定ができません。回避策としては、コンポーネントをクリックした後はプレースホルダーライクを表示しない、入力確認画面を別途用意するなどかと思います。


最後まで読んでいただきありがとうございます！質問やご指摘等ありましたらお気軽にご連絡ください！
「いいね」やフォローもお願いします！！

個人ブログではQiitaに載せきれていない内容もあるので、ご興味ありましたら是非みてください！！！

https://vegcale.com/