@@ -173,6 +173,10 @@ import androidx.compose.ui.text.font.FontStyle
173173import androidx.compose.ui.text.font.FontWeight
174174import androidx.compose.ui.text.style.TextAlign
175175import androidx.compose.ui.text.input.PasswordVisualTransformation
176+ import androidx.compose.foundation.text.KeyboardOptions
177+ import androidx.compose.foundation.text.KeyboardActions
178+ import androidx.compose.ui.text.input.KeyboardType
179+ import androidx.compose.ui.text.input.ImeAction
176180import androidx.compose.ui.text.input.TextFieldValue
177181import androidx.compose.ui.text.input.VisualTransformation
178182import androidx.compose.ui.unit.TextUnit
@@ -681,6 +685,35 @@ private fun RenderTextField(node: ViewNode) {
681685 val label: @Composable () -> Unit = { Text (node.string(" placeholder" ) ? : " " ) }
682686 val transformation = if (node.bool(" secure" ) == true ) PasswordVisualTransformation () else VisualTransformation .None
683687
688+ // .keyboardType chooses the key layout; .submitLabel names the action key.
689+ val keyboardType = when (node.modifiers.firstOrNull { it.kind == " keyboardType" }?.args?.string(" type" )) {
690+ " numberPad" , " asciiCapableNumberPad" -> KeyboardType .Number
691+ " decimalPad" -> KeyboardType .Decimal
692+ " phonePad" , " namePhonePad" -> KeyboardType .Phone
693+ " emailAddress" -> KeyboardType .Email
694+ " URL" -> KeyboardType .Uri
695+ else -> KeyboardType .Text
696+ }
697+ val imeAction = when (node.modifiers.firstOrNull { it.kind == " submitLabel" }?.args?.string(" label" )) {
698+ " search" , " webSearch" -> ImeAction .Search
699+ " go" , " route" -> ImeAction .Go
700+ " send" -> ImeAction .Send
701+ " next" -> ImeAction .Next
702+ " join" , " continue" , " return" -> ImeAction .Done
703+ else -> ImeAction .Default
704+ }
705+ val keyboardOptions = KeyboardOptions (keyboardType = keyboardType, imeAction = imeAction)
706+
707+ // .onSubmit fires on the keyboard action key, whatever its label.
708+ val onSubmit = node.modifiers.firstOrNull { it.kind == " onSubmit" }?.args?.long(" action" )
709+ val keyboardActions = KeyboardActions (
710+ onDone = { onSubmit?.let { SwiftBridge .sink.invokeVoid(it) } },
711+ onGo = { onSubmit?.let { SwiftBridge .sink.invokeVoid(it) } },
712+ onSend = { onSubmit?.let { SwiftBridge .sink.invokeVoid(it) } },
713+ onSearch = { onSubmit?.let { SwiftBridge .sink.invokeVoid(it) } },
714+ onNext = { onSubmit?.let { SwiftBridge .sink.invokeVoid(it) } },
715+ )
716+
684717 // plain drops the box outline; roundedBorder and automatic keep it
685718 if (LocalTextFieldStyle .current == " plain" ) {
686719 TextField (
@@ -689,6 +722,9 @@ private fun RenderTextField(node: ViewNode) {
689722 label = label,
690723 enabled = node.isEnabled(),
691724 visualTransformation = transformation,
725+ keyboardOptions = keyboardOptions,
726+ keyboardActions = keyboardActions,
727+ singleLine = true ,
692728 colors = TextFieldDefaults .colors(
693729 focusedIndicatorColor = Color .Transparent ,
694730 unfocusedIndicatorColor = Color .Transparent ,
@@ -704,6 +740,9 @@ private fun RenderTextField(node: ViewNode) {
704740 label = label,
705741 enabled = node.isEnabled(),
706742 visualTransformation = transformation,
743+ keyboardOptions = keyboardOptions,
744+ keyboardActions = keyboardActions,
745+ singleLine = true ,
707746 modifier = fieldModifier,
708747 )
709748 }
@@ -1572,7 +1611,7 @@ private val KNOWN_MODIFIER_KINDS = setOf(
15721611 " transition" , " focused" , " longPress" , " drag" , " contentMode" , " progressViewStyle" ,
15731612 " buttonStyle" , " pickerStyle" , " toggleStyle" , " textFieldStyle" ,
15741613 " accessibilityLabel" , " accessibilityValue" , " accessibilityHidden" ,
1575- " accessibilityAddTraits" , " accessibilityIdentifier" , " labelStyle" ,
1614+ " accessibilityAddTraits" , " accessibilityIdentifier" , " keyboardType " , " submitLabel " , " onSubmit " , " labelStyle" ,
15761615)
15771616
15781617// Folds a frame entry: fixed size, fill (maxWidth/Height .infinity), bounded
0 commit comments