@@ -34,6 +34,8 @@ import androidx.compose.material3.DropdownMenu
3434import androidx.compose.material3.DropdownMenuItem
3535import androidx.compose.material3.HorizontalDivider
3636import androidx.compose.material3.LinearProgressIndicator
37+ import androidx.compose.material3.MaterialTheme
38+ import androidx.compose.material3.Surface
3739import androidx.compose.material3.ExperimentalMaterial3Api
3840import androidx.compose.material3.Icon
3941import androidx.compose.material3.IconButton
@@ -240,6 +242,10 @@ fun Render(node: ViewNode) {
240242
241243 " TabView" -> RenderTabView (node)
242244
245+ " Form" -> RenderForm (node)
246+
247+ " Section" -> RenderSection (node)
248+
243249 " List" -> RenderList (node)
244250
245251 " LazyVGrid" -> RenderGrid (node, vertical = true )
@@ -313,6 +319,51 @@ private fun RenderStepper(node: ViewNode) {
313319 }
314320}
315321
322+ // A Form scrolls its Sections; non-Section children still render, ungrouped.
323+ @Composable
324+ private fun RenderForm (node : ViewNode ) {
325+ val state = rememberScrollState()
326+ Column (modifier = node.composeModifiers().fillMaxSize().verticalScroll(state).padding(vertical = 8 .dp)) {
327+ RenderChildren (node)
328+ }
329+ }
330+
331+ // An inset, rounded group: uppercase header, rows separated by dividers, footer.
332+ @Composable
333+ private fun RenderSection (node : ViewNode ) {
334+ Column (modifier = Modifier .fillMaxWidth().padding(horizontal = 16 .dp, vertical = 8 .dp)) {
335+ node.string(" header" )?.let {
336+ Text (
337+ it.uppercase(),
338+ fontSize = 13 .sp,
339+ color = MaterialTheme .colorScheme.onSurfaceVariant,
340+ modifier = Modifier .padding(start = 4 .dp, bottom = 6 .dp),
341+ )
342+ }
343+ Surface (
344+ color = MaterialTheme .colorScheme.surfaceVariant,
345+ shape = RoundedCornerShape (10 .dp),
346+ modifier = Modifier .fillMaxWidth(),
347+ ) {
348+ Column {
349+ val rows = node.children.filter { ! it.isPresentation() }
350+ rows.forEachIndexed { index, row ->
351+ if (index > 0 ) HorizontalDivider ()
352+ Box (modifier = Modifier .padding(horizontal = 12 .dp, vertical = 10 .dp)) { Render (row) }
353+ }
354+ }
355+ }
356+ node.string(" footer" )?.let {
357+ Text (
358+ it,
359+ fontSize = 12 .sp,
360+ color = MaterialTheme .colorScheme.onSurfaceVariant,
361+ modifier = Modifier .padding(start = 4 .dp, top = 6 .dp),
362+ )
363+ }
364+ }
365+ }
366+
316367// A trigger button opening a dropdown; each child Button becomes a menu item
317368// firing its own tap callback.
318369@Composable
0 commit comments