JetpackCompose TextFieldについて色々(文字数制限とかも)
ソースコード
@Composable
fun TextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
label: () -> Unit = null,
placeholder: () -> Unit = null,
leadingIcon: () -> Unit = null,
trailingIcon: () -> Unit = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
): @Composable Unit
引数はこの様になっており、色々とカスタムできる様になっている。
例えば中央表示にしたい場合は、
textStyle: にTextAlign.Centerにで中央表示することができる。
状態の変更
composeは基本的に状態を持っていないので、
Valueに変更を通知して状態を変えてあげないと、
状態が追えないのでTextFieldがずっと空白のままになる。
mutableStateで変更可能な状態を作り、
後はなんかややこしいことしなければならない雰囲気である。
状態ホイスティングを使う
流石JetpackComposeというところで、
実はめちゃくちゃ簡単に変更を通知できる。
それが状態ホイスティングである。
var name by rememberSaveable { mutableStateOf("") }
で作ったnameをvalue:に入れ、
onValueChange:に{ name = it }を渡してあげると勝手にやってくれる。
すごく便利。
@Composable
fun HelloScreen() {
var name by rememberSaveable { mutableStateOf("") }
HelloContent(name = name, onNameChange = { name = it })
}
@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Hello, $name",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.h5
)
OutlinedTextField(
value = name,
onValueChange = onNameChange,
label = { Text("Name") }
)
}
}
こんな感じ。
現状maxLengthがない(まじで????)
まじです。
忘れてない???大丈夫????って感じなのですが、
ないものはしょうがないので何かしら対応をしなければなりません.......
maxLengthの打開策
@Composable
fun PinCodeField() {
var text by remember { mutableStateOf("") }
OutlinedTextField(
value = text,
onValueChange = { text = maxLength(it, 1) }
)
}
private fun maxLength(input: String, maxLength: Int) = if (input.length > 1) input.substring(0,maxLength) else input
inputに対してsubstringで制御するという形を現状取っています。
onValueChange に作った関数maxLength(it, 制御したい数)という形。
この方法だけでは日本語入力の場合、文字数を超えたら入力中の文字がリセットされてしまう
OutlinedTextField(
modifier = modifier,
value = name,
onValueChange = {
if (it.length < 5)
name = it
},
日本語機能付きのキーボードは変換が確定するまでは
文字入力が確定してないような挙動になってしまうので
制限している文字数を超えてしまうと
入力中の文字が消えてしまうような挙動になる。
解決方法
var lengthIsOverMaxLength by remember { mutableStateOf(false) }
val view = LocalView.current
OutlinedTextField(
modifier = modifier,
value = creatorComment.value,
onValueChange = valueChange@{
when (lengthIsOverMaxLength) {
true -> {
if (it.length <= 140) {
lengthIsOverMaxLength = false
creatorComment.value = it
}
return@valueChange
}
false -> {
if (it.length > 140) {
lengthIsOverMaxLength = true
// Composeのバグで、文字数制限に達したところで全て文字がクリアされてしまうため、エンターキーイベント発生させる
view.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER))
view.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
return@valueChange
}
creatorComment.value = it
}
}
}
dispatchKeyEventで無理矢理エンターを押すことで回避することができる。
このバグ認知してたんですが書くの忘れてましたホントゴメンナサイ
なにやらこのバグをもっといい方法で解決できるらしい
こちらの記事で詳しく解説してくださっています。
とても参考になるのでこちらの方法をとるのがいいと思います。
参考記事
デザイン参考
Discussion
一つ質問させていただいてもよろしいでしょうか?
この方法だけでは日本語入力の場合、文字数を超えたら入力中の文字がリセットされてしまう
上記のことですが、FlutterのIOSでも起きるのです。
回避方法をご存じでしょうか?
いきなり質問、すみません。
FlutterのiOSだけというのが不可解ですが
とりあえず各ライブラリの最新版を使う
それでも直らなければ
この記事のようにどこで消えているかを特定する必要があるので
恐らくProviderもしくはRiverPodで状態管理をされているでしょうから
そちらの更新メソッドでブレイクポイントをかけてどのタイミングまで生存しているかを確認し、
それがわかれば死なないようにするという感じでしょうか