Compose material3 OverScroll Shows in all 4 directions - android

I am using meterial3 with Compose . I found that all the Scrollable Composable Showing overscroll effect in all 4 directions regardless of scrolling direction. that goes for Column, LazyColumn, LazyVerticalGrid etc ..
I m not using anything custom to override overscroll effect. i can not figure out whats causing this behavior . i will add an example code below with LazyVerticalGrid which is causing this behavior . Since this is vertical grid it should show over scroll only vertically(to and bottom) but it also show it horizontally (left and right)..
Any direction on this will be appreciated.
#Composable
#Destination
fun FavoritesScreen(navigator: DestinationsNavigator) {
val favoritesViewModel: FavoritesViewModel = hiltViewModel()
val favoritesLiveData =
favoritesViewModel.favoritesLiveData.observeAsState(initial = null)
if (favoritesLiveData.value != null) {
if (favoritesLiveData.value!!.isEmpty()) {
// Show empty view
} else {
LazyVerticalGrid(
contentPadding = PaddingValues(
12.dp,
12.dp,
12.dp,
20.dp
),
columns = GridCells.Fixed(2),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
item(span = { GridItemSpan(2) }) {
Spacer(modifier = Modifier.windowInsetsTopHeight(WindowInsets.statusBars))
Text(
text = stringResource(id = R.string.favorites),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(top = 20.dp)
)
ColumnSpacer(value = 10)
}
items(favoritesLiveData.value!!) { game ->
// grid Item
}
}
}
}
}

You may need to update your Compose version. This was a known issue in Compose 1.3.0-alpha01, which was fixed subsequently.

Related

Avoid side padding from particular child item of LazyColumn in jetpack compose

I want to removed side padding of particular child item in LazyColum. I solved this problem in xml with the help of this post. I have same scenario in the jetpack compose. I am using BOM versions of compose_bom = "2022.11.00" with Material 3.
Card(shape = RoundedCornerShape(6.dp),) {
Column(modifier.background(Color.White)) {
LazyColumn(
contentPadding = PaddingValues(all =16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
item {
Text(
text = "Device Header",
modifier = Modifier.padding(top = 10.dp),
style = headerTextStyle
)
}
item {
Divider() // remove padding from side in here
}
}
}
}
Actual Output
Expected Output
In Compose you can't use a negative padding in the children to reduce the padding applied by the parent container. You can use offset modifer with a negative value but it will shift the Divider on the left side.
You can use a layout modifier to apply an horizontal offset increasing the width.
Something like:
LazyColumn(
Modifier.background(Yellow),
contentPadding = PaddingValues(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
//...
item {
val sidePadding = (-8).dp
Divider(modifier = Modifier
.layout { measurable, constraints ->
// Measure the composable adding the side padding*2 (left+right)
val placeable =
measurable.measure(constraints.offset(horizontal = -sidePadding.roundToPx() * 2))
//increase the width adding the side padding*2
layout(
placeable.width + sidePadding.roundToPx() * 2,
placeable.height
) {
// Where the composable gets placed
placeable.place(+sidePadding.roundToPx(), 0)
}
}
)
}
}
Here you can find the output with a Divider() without modifier, and the Divider with the layout modifier.

Compose: What is the proper way to add to a (Lazy)Column enough bottom padding to avoid the FAB to cover its content?

The issue is the very similar to the one discussed here for Flutter, but happens in native Android using Jetpack Compose:
Is there a way to dynamically add bottom padding to a (Lazy)Column so its content is never obscured by the FAB?
The FAB is added to the Scaffold so I would expect some way to get this padding dynamically.
You can try this though this may not be a good solution,
#Composable
fun MyScreen() {
var fabHeight by remember {
mutableStateOf(0)
}
val heightInDp = with(LocalDensity.current) { fabHeight.toDp() }
Scaffold(
floatingActionButton = {
FloatingActionButton(
modifier = Modifier.onGloballyPositioned {
fabHeight = it.size.height
},
shape = CircleShape,
onClick = {},
) {
Icon(imageVector = Icons.Filled.Add, contentDescription = "icon")
}
},
floatingActionButtonPosition = FabPosition.End
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentPadding = PaddingValues(bottom = heightInDp + 16.dp)
) {
items(100) {
Text(text = " Hello world Hello world Hello world Hello world Hello world")
}
}
}
}
Edit: Just simply add the modified paddings to contentPadding of LazyColumn
The hardcoded 16.dp is added because internally the Scaffold implementations has a private property that offsets the fab from the bottom.
So having all of these will produce something like this:

How to set visible indicator' dot and show the others when scrolling in jetpack compose

I have a lazyRow and I want to show list of indicators:
what I want: I want to show 6 items and when user scrolls other indicators get visible.
#Composable
private fun ImagesDotsIndicator(
modifier: Modifier,
totalDots: Int,
selectedIndex: Int
) {
LazyRow(
modifier = modifier,
horizontalArrangement = Arrangement.Center,
reverseLayout = true,
verticalAlignment = Alignment.CenterVertically
) {
if (totalDots == 1) return#LazyRow
items(totalDots) { index ->
if (index == selectedIndex) {
Box(
modifier = Modifier
.size(8.dp)
.clip(CircleShape)
.background(color = Color.White)
)
} else {
Box(
modifier = Modifier
.size(6.dp)
.clip(CircleShape)
.background(color = Color.LightGray)
)
}
Spacer(modifier = Modifier.padding(horizontal = 2.dp))
}
}
}
how can I make this indicator?
I would suggest you use Google's Accompanist HorizontalPager and HorizontalPagerIndicator if you want to swipe pages and show the dots. This is a layout that lays out items in a horizontal row, and allows the user to horizontally swipe between pages and also show the page indicator.
You need to add these 2 lines to your app build gradle file to add the dependencies.
// Horizontal Pager and Indicators - Accompanist
implementation "com.google.accompanist:accompanist-pager:0.24.7-alpha"
implementation "com.google.accompanist:accompanist-pager-indicators:0.24.7-alpha"
On your composable file, you can add a simple Sealed class to hold the data that you want to display e.g. text.
sealed class CustomDisplayItem(val text1:String, val text2: String){
object FirstItem: CustomDisplayItem("Hi", "World")
object SecondItem: CustomDisplayItem("Hello", "I'm John")
}
Thereafter make a template of the composable element or page that you want to show if the user swipes left or right.
#Composable
fun DisplayItemTemplate(item: CustomDisplayItem) {
Column() {
Text(text = item.text2 )
Spacer(modifier = Modifier.height(4.dp))
Text(text = item.text2)
}
}
Lastly use HorizontalPager and HorizontalPageIndicator composables to display the corresponding page when a user swipes back and forth.
#OptIn(ExperimentalPagerApi::class)
#Composable
fun ImagesDotsIndicator(
modifier: Modifier,
) {
//list of pages to display
val displayItems = listOf(CustomDisplayItem.FirstItem, CustomDisplayItem.SecondItem)
val state = rememberPagerState()
Column(modifier = modifier.fillMaxSize()) {
//A horizontally scrolling layout that allows users to
// flip between items to the left and right.
HorizontalPager(
count = 6,
state = state,
) {
/*whenever we scroll sideways the page variable changes
displaying the corresponding page */
item ->
//call template item and add the data
DisplayItemTemplate(item = displayItems[item])
}
//HorizontalPagerIndicator dots
HorizontalPagerIndicator(
pagerState = state,
activeColor = MaterialTheme.colors.primary,
inactiveColor = Color.Gray,
indicatorWidth = 16.dp,
indicatorShape = CircleShape,
spacing = 8.dp,
modifier = Modifier
.weight(.1f)
.align(CenterHorizontally)
)
}
}
Please see the above links to read more on how you can customize your composables to work in your case.
Actually it is preaty straight forward without any additional library:
val list = (0..100).toList()
val state = rememberLazyListState()
val visibleIndex by remember {
derivedStateOf {
state.firstVisibleItemIndex
}
}
Text(text = visibleIndex.toString())
LazyColumn(state = state) {
items(list) { item ->
Text(text = item.toString())
}
}
Create scroll state and use it on your list, and on created scroll state observe first visible item.

Android compose LazyVerticalGrid/Column weird behavior

I have this weird behavior when using compose lazy column or LazyVerticalGrid
When scrolling to the top the lazy grid gets separated from the status bar
How can i prevent that ?
Giving the lazy grid a background can solve the issue but that's not what I want !
My Code
LazyVerticalGrid(
modifier = modifier
.fillMaxWidth()
.height(500.dp),
columns = GridCells.Adaptive(120.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
item(span = {
GridItemSpan(maxLineSpan)
}) {
Header()
}
items(items = historyAppointments) { item ->
AppointmentCard(item)
}
}

Android Jetpack Compose position children in LazyRow

I have a sequence of items I want to show as list of items in a Track. Only thing is, at the beginning, items have to be lined starting from middle of the track. Afterwards, the items can be scrolled normally.
e.g. at beginning :
MyTrack : [----blankSpace 50%-------[dynamic item1[[dynamic item2][dynamic item3]--]
after scrolling when all items are visible for example:
MyTrack[[item1][item2][item3][item4][item5]]
This Row has to be scrollable and each item could have varying width.
This is the item on the track:
data class MyItem(val widthFactor: Long, val color: Color)
Question : is there. a way to give start position of the items in the LazyRow ? Or is there a better Layout I should use in Jetpack Compose ?
A layout like LazyRow() won't work because there is no way to tell to start lining up items from middle of it.
I can use something like Canvas and drawRect of items in it but then I need to implement the swipe and scroll features like in LazyRow.
Thanks in advance.
You can use spacer item with .fillParentMaxWidth which is available for LazyList items:
LazyRow(
modifier = Modifier.fillMaxWidth(),
) {
item {
Spacer(modifier = Modifier.fillParentMaxWidth(0.5f))
}
// Your other items
}
This is better than the other two provided solutions - Configuration.screenWidthDp is only usable when your LazyRow fills whole screen width which is not always the case, BoxWithConstraints adds complexity that is not necessary here.
Use this to get screen width
val configuration = LocalConfiguration.current
val screenWidth = configuration.screenWidthDp.dp
Source - https://stackoverflow.com/a/68919901/9636037
Code
#Composable
fun ScrollableRowWithSpace() {
val configuration = LocalConfiguration.current
val screenWidth = configuration.screenWidthDp.dp
val list = Array(10) {
"Item ${it + 1}"
}
LazyRow(
modifier = Modifier
.background(LightGray)
.fillMaxWidth(),
) {
item {
Spacer(modifier = Modifier
.background(White)
.width(screenWidth / 2))
}
items(list) {
Text(
text = it,
modifier = Modifier
.padding(16.dp)
.background(White),
)
}
}
}
I would recommend following solution you can use it anywhere and get half of the width.
data class MyItem(val widthFactor: Long, val color: Color)
#Composable
fun LazyRowWithPadding() {
val myItemList = mutableListOf(
MyItem(12345, Color.Blue),
MyItem(12345, Color.Cyan),
MyItem(12345, Color.Green),
MyItem(12345, Color.Gray),
MyItem(12345, Color.Magenta),
)
BoxWithConstraints(Modifier.fillMaxSize()) {
LazyRow(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(start = maxWidth / 2)
) {
items(myItemList) {
Box(
Modifier
.padding(end = 8.dp)
.background(it.color)
.padding(24.dp)) {
Text(text = it.widthFactor.toString())
}
}
}
}
}
Example

Categories

Resources