Skip to content

Commit 5944fe8

Browse files
committed
Render a navigation search field bound to Swift
1 parent e31abe6 commit 5944fe8

1 file changed

Lines changed: 42 additions & 0 deletions

File tree

  • Demo/swiftui/src/commonMain/kotlin/com/pureswift/swiftui

Demo/swiftui/src/commonMain/kotlin/com/pureswift/swiftui/Render.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,13 +1243,55 @@ private fun RenderNavStack(node: ViewNode) {
12431243
label = "nav",
12441244
) { index ->
12451245
LayoutColumn(modifier = Modifier.padding(padding)) {
1246+
node.searchField(index)?.let { RenderSearchField(it, index) }
12461247
node.children.getOrNull(index)?.let { Render(it) }
12471248
}
12481249
}
12491250
}
12501251
RenderSheetsAndAlerts(node.children.getOrNull(topIndex))
12511252
}
12521253

1254+
private class SearchFieldSpec(val text: String, val callbackId: Long, val prompt: String)
1255+
1256+
// A screen's `searchable` descriptor: [text, callbackId, prompt], or an empty
1257+
// array when that screen declared none.
1258+
private fun ViewNode.searchField(index: Int): SearchFieldSpec? {
1259+
val searches = props["searches"] as? kotlinx.serialization.json.JsonArray ?: return null
1260+
val entry = searches.getOrNull(index) as? kotlinx.serialization.json.JsonArray ?: return null
1261+
if (entry.size < 3) return null
1262+
val text = (entry[0] as? kotlinx.serialization.json.JsonPrimitive)?.content ?: ""
1263+
val id = (entry[1] as? kotlinx.serialization.json.JsonPrimitive)?.content?.toLongOrNull() ?: return null
1264+
val prompt = (entry[2] as? kotlinx.serialization.json.JsonPrimitive)?.content ?: ""
1265+
return SearchFieldSpec(text, id, prompt)
1266+
}
1267+
1268+
// Same uncontrolled-with-reconciliation scheme as RenderTextField, keyed by the
1269+
// stable screen index (the callback id churns each evaluation, so it can't key
1270+
// the remembered cursor state).
1271+
@Composable
1272+
private fun RenderSearchField(spec: SearchFieldSpec, index: Int) {
1273+
var local by remember(index) { mutableStateOf(TextFieldValue(spec.text)) }
1274+
var lastSent by remember(index) { mutableStateOf(spec.text) }
1275+
if (spec.text != lastSent) {
1276+
local = TextFieldValue(spec.text, selection = androidx.compose.ui.text.TextRange(spec.text.length))
1277+
lastSent = spec.text
1278+
}
1279+
OutlinedTextField(
1280+
value = local,
1281+
onValueChange = { v ->
1282+
local = v
1283+
if (v.text != lastSent) {
1284+
lastSent = v.text
1285+
SwiftBridge.sink.invokeString(spec.callbackId, v.text)
1286+
}
1287+
},
1288+
placeholder = { Text(spec.prompt.ifEmpty { "Search" }) },
1289+
leadingIcon = { Icon(Icons.Filled.Search, contentDescription = null) },
1290+
singleLine = true,
1291+
modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 4.dp),
1292+
)
1293+
}
1294+
12531295
@Composable
12541296
private fun RenderTabView(node: ViewNode) {
12551297
val selection = node.long("selection")?.toInt() ?: 0

0 commit comments

Comments
 (0)