Column does not vertically scroll although it was configured to - android-jetpack-compose

I am trying to add scrolling behaviour to a Column by setting verticalScroll(state = rememberScrollState()) modifier.
I checked out some examples in the official compose-jb repository, and it seems that that is the right way to do it, yet in my case the content is not scrollable.
Here the full code:
#Composable
#Preview
fun App() {
MaterialTheme {
// add scroll behaviour
val stateVertical = rememberScrollState(0)
Column(modifier = Modifier.verticalScroll(state = stateVertical)) {
repeat(100){
Text("item: $it")
}
}
}
}
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
App()
}
}
Any ideas why it does not work in my case?
The Column is populated with 100 Text items, more than enough to exceed the default window height.

It actually works!
For some reason I was trying to use click and drag... which lead me to confusion.

Related

Android Jetpack Compose statusbar height?

How can I get the statusbar height?
Accompanist Insets is now deprecated, and WindowInsets.systemBars.asPaddingValues() gives me 0 every time...
And Modifier.statusBarsPadding() does nothing in my layout.
Is there any way to get this height?!?
When i call WindowCompat.setDecorFitsSystemWindows(window, false) before setContent on Activity i get correct values otherwise it returns 0
#Composable
private fun MyComposable() {
Column {
val paddingValues = WindowInsets.systemBars.asPaddingValues()
Spacer(modifier=Modifier.height(paddingValues.calculateTopPadding()))
Text(text = "Padding values: $paddingValues")
}
}
Result
Without WindowCompat.setDecorFitsSystemWindows(window, false)
You should be able to get the statusbar height using:
val statusBarHeight = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
If you want to use the height just as a modifier:
(From the accompanist migration table: https://google.github.io/accompanist/insets/#migration-table)
Modifier.windowInsetsTopHeight(WindowInsets.statusBars)

AdManagerAdView not rendering ad image when off screen in LazyColumn

I am wrapping an AdManagerAdView in an AndroidView so I can use it in Jetpack Compose. The image fails to load when I use it in a LazyColumn AND the AdManagerAdView tries to load the image before the composable is on screen.
If I scroll quickly to that element, so LazyColumn composes it AND it is on screen before the image comes back from the ad server, it works as expected.
LazyColumn {
items(5) {
SomeOtherComposable(it)
}
item {
AndroidView(
modifier = Modifier
.width(300.dp)
.height(250.dp)
.background(Color.Green),
factory = { context ->
val adView = AdManagerAdView(context)
adView.adSize = AdSize.MEDIUM_RECTANGLE
adView.adUnitId = adUnitId
adView.loadAd(Builder().build())
adView
}
)
}
items(5) {
SomeOtherComposable(it)
}
}
For demo purposes...
#Composable
fun SomeOtherComposable(i: Int) {
Text(
text = "SomeOtherComposable $i",
modifier = Modifier
.fillMaxWidth()
.height(320.dp)
.background(Color.White)
)
}
This also works fine if the wrapped AdManagerAdView is used in a non-lazy Column or any other Compose layout.
This feels like a weird timing thing in LazyColumn that just happens to manifest when the Composable isn't on screen yet since using it in a regular Column works fine under the same scenario.
Has anyone experienced anything like this?
SOLVED
See my answer below
Ok, the issue is actually that both factory{} and update{} are called before the AndroidView has gone through the layout pass. In my steps to reproduce, the ad image is coming back and being added to the internal view before it has a measured width and height.
The solution is to delay that load call until after the layout pass using doOnLayout{} like so:
AndroidView(
modifier = Modifier
.width(300.dp)
.height(250.dp)
.background(Color.Green),
factory = { context ->
val adView = AdManagerAdView(context)
adView.adSize = AdSize.MEDIUM_RECTANGLE
adView
},
update = { adView ->
adView.adUnitId = adUnitId
adView.doOnLayout {
adView.loadAd(Builder().build())
}
}
)

AnimatedVisibility and BringIntoViewRequester not working together

I'm building an expandable Composable which would be expanded when clicked.
This would be implemented by using the AnimatedVisibility which works perfectly.
Code for the visibility animation:
AnimatedVisibility(
visible = isExpanded,
) {
// Content removed.
}
The problem I'm currently facing is that this is located in a vertical scrollable column and it should scroll to the expanded content when clicked next to expanding it.
As I read this would be done by using the BringIntoViewRequester as in the code snippet below:
var isExpanded by remember { mutableStateOf(false) }
val intoViewRequester = remember { BringIntoViewRequester() }
ClickableComposable(modifier = Modifier.clickable {
isExpanded = !isExpanded
if(isExpanded) {
coroutineScope.launch {
// delay(200)
intoViewRequester.bringIntoView(rect = null)
}
}
})
AnimatedVisibility(
modifier = Modifier.bringIntoViewRequester(intoViewRequester),
visible = isExpanded,
) {
// Content removed.
}
The code above works with the delay but that's not a perfect interaction for the user. To first see the content expanding and afterwards see the page scroll. The ideal situation would be that it would happen at the same time, however the content is not yet measured in any way. By removing the delay it does not work as the content is not yet visible.
Is there anything in Compose to do the expanding and scrolling to at the same time?

Is ScrollableTabRow lazy

If i create like 1000 Tabs within the ScrollableTabRow will it only compose the Tabs which are visible to the screen or`will compose everytime non-visible Tabs if something changes?
Like same as a LazyColumn composes only visible items.
I've written a minimal example in which i'm just "repeat"ing the same composable a lot of times - with no item{}-like DSL available like the lazy-components have.
#Composable
#Preview
fun MinimalTabExample() {
var selectedTabIndex by remember { mutableStateOf(0) }
ScrollableTabRow(selectedTabIndex = selectedTabIndex) {
repeat(60) { tabNumber ->
Tab(
selected = selectedTabIndex == tabNumber,
onClick = { selectedTabIndex = tabNumber },
text = { Text(text = "Tab #$tabNumber") }
)
}
}
}
Also, if you look into the ScrollableTabRow component you'll find that all the tabs are measured right at the beginning: (tabs are the compose-tabs created above)
val tabPlaceables = subcompose(TabSlots.Tabs, tabs)
.map { it.measure(tabConstraints) }
I guess this is implemented this way due to the height-calculation: "Make the height as high as the highest component"
tabPlaceables.forEach {
layoutWidth += it.width
layoutHeight = maxOf(layoutHeight, it.height)
}

AnimatedVisibility breaks constraints in ConstraintLayout in Jetpack Compose

Edit: Also happens when I swap ConstraintLayout for a Box using alignment as well...
Seems like AnimatedVisibility doesn't play well with ConstraintLayout in JP Compose at the moment.
AnimatedVisibility(
visible = entryListState.firstVisibleItemIndex > 3,
enter = fadeIn() + expandIn(expandFrom = Alignment.Center),
exit = fadeOut() + shrinkOut(shrinkTowards = Alignment.Center)
) {
ExtendedFloatingActionButton(
modifier = Modifier.constrainAs(scrollToTop) {
start.linkTo(parent.start)
bottom.linkTo(parent.bottom)
},
text = { Text(text = "TOP") },
onClick = { scope.launch { entryListState.animateScrollToItem(0) } }
)
}
The ( TOP ) fab should appear in the bottom left corner, but instead I get
When I remove the AnimatedVisibility wrapper, everything works fine. :( I know I can work around this, but I'm curious if there's something I'm doing incorrectly with the configuration of the AnimatedVisibility composable?
Turns out I was thinking about AnimatedVisibility all wrong. It's just another Composable. I needed to lift the FAB's modifiers re: positioning to the AnimatedVisibilty's modifiers.

Resources