LazyVStack nested inside a ScrollView works as expected on iOS (lazy rows feed into the parent scroller) but on Android produces two competing scroll regions: content above the LazyVStack scrolls independently from the LazyVStack itself. No compile-time or transpile-time warning is emitted; the divergence only surfaces at runtime during UX.
Repro:
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Text("Header section").font(.headline)
ForEach(0..<5) { i in
Text("Header row \(i)")
}
LazyVStack(alignment: .leading, spacing: 8) {
Text("List section").font(.headline)
ForEach(0..<50) { i in
Text("List row \(i)")
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(Color.gray.opacity(0.1))
}
}
}
.padding()
}
}
}
Expected (iOS behavior)
Single scroll region. The header rows scroll off the top of the screen as the user pulls the list rows into view. LazyVStack lazily materializes its rows but defers scrolling to the parent ScrollView.
Actual (Android behavior)
Two independent scroll regions. The header section stays in place while the LazyVStack scrolls within its own bounded region — and vice versa, scrolling outside the LazyVStack doesn't move the lazy content.
Diagnosis
It looks like LazyVStack is being translated to Compose's LazyColumn, which is itself a scroll container with its own viewport. This may be an anti-pattern in Android.
Suggested fix
When LazyVStack appears inside a ScrollView, translate it to a plain Column rather than LazyColumn, giving up laziness on Android (acceptable: it matches what one would write by hand to avoid the nesting issue) in exchange for correct scroll behavior matching iOS. Or alternatively, emit a transpile-time warning so the divergence isn't silent.
The current workaround in user code is to swap LazyVStack → VStack, which works but defeats the purpose of LazyVStack for users who genuinely want lazy loading on long lists.
LazyVStack nested inside a ScrollView works as expected on iOS (lazy rows feed into the parent scroller) but on Android produces two competing scroll regions: content above the LazyVStack scrolls independently from the LazyVStack itself. No compile-time or transpile-time warning is emitted; the divergence only surfaces at runtime during UX.
Repro:
Expected (iOS behavior)
Single scroll region. The header rows scroll off the top of the screen as the user pulls the list rows into view. LazyVStack lazily materializes its rows but defers scrolling to the parent ScrollView.
Actual (Android behavior)
Two independent scroll regions. The header section stays in place while the LazyVStack scrolls within its own bounded region — and vice versa, scrolling outside the LazyVStack doesn't move the lazy content.
Diagnosis
It looks like LazyVStack is being translated to Compose's LazyColumn, which is itself a scroll container with its own viewport. This may be an anti-pattern in Android.
Suggested fix
When LazyVStack appears inside a ScrollView, translate it to a plain Column rather than LazyColumn, giving up laziness on Android (acceptable: it matches what one would write by hand to avoid the nesting issue) in exchange for correct scroll behavior matching iOS. Or alternatively, emit a transpile-time warning so the divergence isn't silent.
The current workaround in user code is to swap LazyVStack → VStack, which works but defeats the purpose of LazyVStack for users who genuinely want lazy loading on long lists.