【Jetpack Compose】OutlinedTextField 全体に対して onClick する方法

# 完成形（OutlinedTextField をクリックすると日付選択ができるようにする）

![OutlinedTextFieldAndDatePicker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1630025/053364ae-08ca-4287-325c-338e56bc4096.gif)


- 日付を選択すると　OutlinedTextField の値を変更する
    - 日付選択はダイアログから行う
    - テキスト入力はさせない
- 日付選択のダイアログを閉じると OutlinedTextField のフォーカスを外す


# 実装

## OutlinedTextFiled 全体を選択可能にする

<details><summary>【NG】 Modifier.fillMaxWidth().clickable(onClick)</summary>

```kotlin
    OutlinedTextField(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = { /* TODO */ }),
        value = value,
        onValueChange = onValueChange,
        placeholder = { Text(text = "選択してください") },
        trailingIcon = {
            Icon(
                imageVector = GeneratedVectorImages.IcnPull,
                contentDescription = null,
            )
        },
    )
```

OutlinedTextField 全体に対してクリックしようと `Modifier.fillMaxWidth().clickable(onClick)` をしてもクリックできませんので注意が必要です。

</details>


ポイント

- interactionSource を設定する
- readOnly = true　を設定する

```kotlin
@Composable
fun SelectOutlineTextField(
    value: String,
    onValueChange: (String) -> Unit,
    onClick: () -> Unit,
) {
    val interactionSource = remember {
        object : MutableInteractionSource {
            override val interactions = MutableSharedFlow<Interaction>(
                extraBufferCapacity = 16,
                onBufferOverflow = BufferOverflow.DROP_OLDEST,
            )

            override suspend fun emit(interaction: Interaction) {
                when (interaction) {
                    is PressInteraction.Press -> {
                        onClick()
                    }
                }

                interactions.emit(interaction)
            }

            override fun tryEmit(interaction: Interaction): Boolean {
                return interactions.tryEmit(interaction)
            }
        }
    }

    OutlinedTextField(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 16.dp),
        value = value,
        onValueChange = onValueChange,
        placeholder = { Text(text = "選択してください") },
        trailingIcon = {
            Icon(
                imageVector = Icons.Filled.KeyboardArrowDown,
                contentDescription = null,
            )
        },
        readOnly = true,
        interactionSource = interactionSource,
    )
}
```

## 日付選択画面

```kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DatePickerModal(
    onDateSelected: (Long?) -> Unit,
    onDismiss: () -> Unit
) {
    val datePickerState = rememberDatePickerState()

    DatePickerDialog(
        onDismissRequest = onDismiss,
        confirmButton = {
            TextButton(onClick = {
                onDateSelected(datePickerState.selectedDateMillis)
                onDismiss()
            }) {
                Text("OK")
            }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) {
                Text("Cancel")
            }
        }
    ) {
        DatePicker(state = datePickerState)
    }
}
```

## 完成形

```kotlin
@Composable
fun HomeScreen() {
    var dateText by remember { mutableStateOf("") }
    var showDialog by remember { mutableStateOf(false) }

    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        SelectOutlineTextField(
            value = dateText,
            onValueChange = { dateText = it },
            onClick = { showDialog = true },
        )
    }
    
    val focusManager = LocalFocusManager.current

    if (showDialog) {
        DatePickerModal(
            onDateSelected = {
                dateText = convertMillisToDate(it)
                showDialog = false
                focusManager.clearFocus()
            },
            onDismiss = {
                showDialog = false
                focusManager.clearFocus()
            },
        )
    }
}

private fun convertMillisToDate(millis: Long?): String {
    val formatter = SimpleDateFormat("MM/dd/yyyy", Locale.getDefault())
    return millis?.let {
        formatter.format(Date(it))
    } ?: ""
}

```

# 参考資料


https://github.com/JetBrains/compose-multiplatform/issues/220

https://developer.android.com/develop/ui/compose/components/datepickers