@@ -54,6 +54,7 @@ import androidx.compose.foundation.layout.Column as LayoutColumn
5454import androidx.compose.foundation.layout.fillMaxSize
5555import androidx.compose.foundation.layout.height
5656import androidx.compose.foundation.lazy.LazyColumn
57+ import androidx.compose.foundation.lazy.LazyRow
5758import androidx.compose.foundation.lazy.grid.GridCells
5859import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
5960import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
@@ -306,14 +307,25 @@ private fun RenderResolved(node: ViewNode) {
306307 " Divider" -> HorizontalDivider (modifier = node.composeModifiers())
307308
308309 " ScrollView" -> {
309- val state = rememberScrollState()
310- if (node.string(" axis" ) == " horizontal" ) {
311- Row (modifier = node.composeModifiers().horizontalScroll(state)) { RenderChildren (node) }
310+ // A lazy stack scrolls itself, and nesting it inside a scrolling
311+ // parent would measure it with unbounded height (a Compose
312+ // crash), so hand the scrolling over to it.
313+ val lone = node.children.singleOrNull { ! it.isPresentation() }
314+ if (lone != null && lone.isLazyStack()) {
315+ RenderChild (lone)
312316 } else {
313- Column (modifier = node.composeModifiers().verticalScroll(state)) { RenderChildren (node) }
317+ val state = rememberScrollState()
318+ if (node.string(" axis" ) == " horizontal" ) {
319+ Row (modifier = node.composeModifiers().horizontalScroll(state)) { RenderChildren (node) }
320+ } else {
321+ Column (modifier = node.composeModifiers().verticalScroll(state)) { RenderChildren (node) }
322+ }
314323 }
315324 }
316325
326+ " LazyVStack" -> RenderLazyStack (node, vertical = true )
327+ " LazyHStack" -> RenderLazyStack (node, vertical = false )
328+
317329 // A Color greedily fills the space offered it (SwiftUI semantics);
318330 // fillMaxWidth is applied after the chain so an explicit frame width
319331 // still constrains it.
@@ -1554,6 +1566,54 @@ private fun ViewNode.stringArray(key: String): List<String> {
15541566}
15551567
15561568
1569+ private fun ViewNode.isLazyStack (): Boolean = type == " LazyVStack" || type == " LazyHStack"
1570+
1571+ // A lazy stack fetches only the elements Compose asks for, one JNI call each.
1572+ // Content that wasn't a single ForEach carries no provider and renders eagerly.
1573+ @Composable
1574+ private fun RenderLazyStack (node : ViewNode , vertical : Boolean ) {
1575+ val provider = node.long(" itemProvider" )
1576+ if (provider == null ) {
1577+ if (vertical) Column (modifier = node.composeModifiers()) { RenderChildren (node) }
1578+ else Row (modifier = node.composeModifiers()) { RenderChildren (node) }
1579+ return
1580+ }
1581+ val count = node.count ? : 0
1582+ val keys = node.stringArray(" keys" )
1583+ val spacing = (node.double(" spacing" ) ? : 0.0 ).dp
1584+ if (vertical) {
1585+ LazyColumn (
1586+ modifier = node.composeModifiers().fillMaxWidth(),
1587+ horizontalAlignment = when (node.string(" alignment" )) {
1588+ " leading" -> Alignment .Start
1589+ " trailing" -> Alignment .End
1590+ else -> Alignment .CenterHorizontally
1591+ },
1592+ verticalArrangement = Arrangement .spacedBy(spacing),
1593+ ) {
1594+ items(count = count, key = { keys.getOrNull(it) ? : it }) { index ->
1595+ val element = remember(provider, index) { SwiftBridge .sink.itemNode(provider, index) }
1596+ element?.let { RenderChild (it) }
1597+ }
1598+ }
1599+ } else {
1600+ LazyRow (
1601+ modifier = node.composeModifiers(),
1602+ verticalAlignment = when (node.string(" alignment" )) {
1603+ " top" -> Alignment .Top
1604+ " bottom" -> Alignment .Bottom
1605+ else -> Alignment .CenterVertically
1606+ },
1607+ horizontalArrangement = Arrangement .spacedBy(spacing),
1608+ ) {
1609+ items(count = count, key = { keys.getOrNull(it) ? : it }) { index ->
1610+ val element = remember(provider, index) { SwiftBridge .sink.itemNode(provider, index) }
1611+ element?.let { RenderChild (it) }
1612+ }
1613+ }
1614+ }
1615+ }
1616+
15571617@OptIn(ExperimentalMaterial3Api ::class )
15581618@Composable
15591619private fun RenderList (node : ViewNode ) {
0 commit comments