I am working on recreating facebook reactions with jetpack compose. Have now an issue with how I can detect when hovering over an item in a row and scale it? To be more clear I am creating THIS. I reused popup from jetpack compose that part is fine. But dragging/hovering over the row not sure how to scale images. Any advice or help?
I did this initial implementation for you. I hope you can continue your implementation from here:
#ExperimentalComposeUiApi
#Composable
fun ReactionsComponent() {
val density = LocalDensity.current
var selectedIndex by remember {
mutableStateOf(-1)
}
val iconSize = 48.dp
val boxPadding = 8.dp
val iconSizePx = with(density) { iconSize.toPx() }
val boxPaddingPx = with(density) { boxPadding.toPx() }
val increaseSize = iconSize.times(2f)
val icons = listOf(
Icons.Default.Favorite,
Icons.Default.Star,
Icons.Default.Call,
Icons.Default.AccountBox,
Icons.Default.ThumbUp
)
Box(
Modifier
.height(increaseSize)
.width(IntrinsicSize.Min)
.pointerInteropFilter {
val selection = ((it.x - boxPaddingPx) / iconSizePx).toInt()
if (selection >= icons.size || selection < 0 || it.x < boxPaddingPx) {
selectedIndex = -1
} else if (it.action == MotionEvent.ACTION_UP) {
selectedIndex = -1 // finger released
} else {
selectedIndex = selection
}
true
}
) {
Box(
Modifier
.align(Alignment.BottomStart)
.fillMaxWidth()
.height(iconSize + boxPadding.times(2))
.background(Color.LightGray, CircleShape)
)
Row(
Modifier
.align(Alignment.BottomStart)
.width(IntrinsicSize.Min)
.padding(boxPadding),
verticalAlignment = Alignment.Bottom
) {
icons.forEachIndexed { index, icon ->
val size = if (selectedIndex == index) increaseSize else iconSize
Box(
Modifier
.border(1.dp, Color.LightGray, CircleShape)
.background(Color.White, CircleShape)
.height(animateDpAsState(size).value)
.width(animateDpAsState(size).value)
) {
Icon(
icon,
contentDescription = null,
tint = Color.Magenta,
modifier = Modifier.fillMaxSize().padding(8.dp)
)
}
}
}
}
}
Here's the result:
You can find the full code here (which includes item selection).
Related
What I want
Top fixed item with status bar padding and adaptive radius
Bottom fixed item with navigation bar padding
Adaptive center item, have enough room => Not scrollable, if not => scrollable
Current status
For full demo click this link
Only problem here is when there is no extra space, and we are dragging the scrollable list.
I think it's a bug, because everything is fine except the scrollable column.
window.height - sheetContent.height >= statusBarHeight
Everything is fine.
window.height - sheetContent.height < statusBarHeight
Dragging top fixed item or bottom fixed item, scroll still acting well.
Dragging the scrollable list, sheet pops back to top when the sheetState.offset is approaching statusBarHeight
Test youself
You can test it with these 3 functions, for me, I'm using Pixel 2 Emulator, itemCount at 18,19 could tell the difference.
#Composable
fun CEModalBottomSheet(
sheetState: ModalBottomSheetState,
onCloseDialogClicked: () -> Unit,
title: String = "BottomSheet Title",
toolbarElevation: Dp = 0.dp,
sheetContent: #Composable ColumnScope.() -> Unit,
) {
val density = LocalDensity.current
val sheetOffset = sheetState.offset
val statusBar = WindowInsets.statusBars.asPaddingValues()
val shapeRadius by animateDpAsState(
if (sheetOffset.value < with(density) { statusBar.calculateTopPadding().toPx() }) {
0.dp
} else 12.dp
)
val dynamicStatusBarPadding by remember {
derivedStateOf {
val statusBarHeightPx2 = with(density) { statusBar.calculateTopPadding().toPx() }
val offsetValuePx = sheetOffset.value
if (offsetValuePx >= statusBarHeightPx2) {
0.dp
} else {
with(density) { (statusBarHeightPx2 - offsetValuePx).toDp() }
}
}
}
ModalBottomSheetLayout(
sheetState = sheetState,
sheetShape = RoundedCornerShape(topStart = shapeRadius, topEnd = shapeRadius),
content = {},
sheetContent = {
Column(modifier = Modifier.fillMaxWidth()) {
TopTitleItemForDialog(
title = title,
elevation = toolbarElevation,
topPadding = dynamicStatusBarPadding,
onClick = onCloseDialogClicked
)
sheetContent()
}
})
}
#Composable
fun TopTitleItemForDialog(
title: String,
elevation: Dp = 0.dp,
topPadding: Dp = 0.dp,
onClick: () -> Unit
) {
Surface(
modifier = Modifier.fillMaxWidth(),
color = Color.LightGray,
elevation = elevation
) {
Row(
modifier = Modifier.padding(top = topPadding),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.size(16.dp))
Text(
text = title,
maxLines = 1,
modifier = Modifier.weight(1f)
)
IconButton(onClick = onClick) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(R.string.cancel),
tint = Color.Gray,
modifier = Modifier.size(24.dp)
)
}
}
}
}
class SheetPaddingTestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.statusBarColor = android.graphics.Color.TRANSPARENT
window.navigationBarColor = android.graphics.Color.TRANSPARENT
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
Box(
Modifier
.fillMaxSize()
.background(Color.Green), contentAlignment = Alignment.Center
) {
var itemCount by remember { mutableStateOf(20) }
val state = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) { false }
val scope = rememberCoroutineScope()
Text("显示弹窗", modifier = Modifier.clickable {
scope.launch { state.animateTo(ModalBottomSheetValue.Expanded) }
})
CEModalBottomSheet(sheetState = state,
onCloseDialogClicked = {
scope.launch {
state.hide()
}
}, sheetContent = {
Column(
Modifier
.verticalScroll(rememberScrollState())
.weight(1f, fill = false)
.padding(horizontal = 16.dp),
) {
repeat(itemCount) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(30.dp)
.background(Color.Blue.copy(alpha = 1f - it * 0.04f))
)
}
}
CompositionLocalProvider(
LocalContentColor.provides(Color.White)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colors.primary)
.padding(vertical = 12.dp)
.navigationBarsPadding(),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(modifier = Modifier.weight(1f),
onClick = {
itemCount = max(itemCount - 1, 0)
}) {
Icon(Icons.Default.KeyboardArrowLeft, "")
}
Text(
modifier = Modifier.weight(1f), text = "$itemCount",
textAlign = TextAlign.Center
)
IconButton(modifier = Modifier.weight(1f),
onClick = {
itemCount++
}) {
Icon(Icons.Default.KeyboardArrowRight, "")
}
}
}
}
)
}
}
}
}
I have this logic with which I can already change the background alpha as the user scrolls, but it's not exactly what I want. I want the alpha to start decreasing until it disappears after the user has scrolled 1.5 of the screen. How can I do this?
val list = mutableListOf<String>().apply {
for (i in 0..1000) {
add("Item $i")
}
}
setContent {
val lazyListState = rememberLazyListState()
val visibility by remember {
derivedStateOf {
when {
lazyListState.layoutInfo.visibleItemsInfo.isNotEmpty() && lazyListState.firstVisibleItemIndex == 0 -> {
val imageSize = lazyListState.layoutInfo.visibleItemsInfo[0].size
val scrollOffset = lazyListState.firstVisibleItemScrollOffset
scrollOffset / imageSize.toFloat()
}
else -> 1f
}
}
}
ScrollableBackgroundTheme {
// A surface container using the 'background' color from the theme
Box {
Image(
painter = painterResource(id = R.drawable.main_background),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.graphicsLayer {
alpha = 1f - visibility
}
)
LazyColumn(
state = lazyListState
) {
items(list.size) { index ->
Text(text = list[index], modifier = Modifier.padding(20.dp))
}
}
}
}
}
I have my composable which I am displaying the content according to some states. First time I open the screen everything works fine but when I move my app to background and then come back it stops being recomposed. When I try to debug it I can see that the state is changing but for some reason my view is not being updated. Here is my composable:
val coroutineScope = rememberCoroutineScope()
val scrollState = rememberScrollState()
val isKeyboardOpen by keyboardAsState()
var buttonHeight by remember { mutableStateOf(0.dp) }
Column(
modifier = Modifier
.fillMaxSize()
.imePadding()
.verticalScroll(scrollState)
.background(color = White),
) {
Text(
modifier = Modifier.padding(
start = 18.dp,
end = 18.dp,
top = 22.dp,
bottom = 12.dp
),
text = "text",
style = MaterialTheme.typography.subtitle1,
color = MaterialTheme.colors.onSurface
)
BasicTextField(
modifier = Modifier
.padding(horizontal = 18.dp)
.onGloballyPositioned { coordinates ->
if (isKeyboardOpen == Keyboard.Opened) {
coroutineScope.launch {
scrollState.animateScrollTo(coordinates.positionInParent().y.roundToInt())
}
}
},
onValueChange = { onEvent.invoke(Event.ReasonFieldValueChanged(it)) },
onFocused = { onEvent.invoke(Event.ReasonFieldFocused) }
)
Card(
modifier = Modifier.padding(horizontal = 10.dp, vertical = 24.dp),
backgroundColor = MaterialTheme.colors.background,
elevation = 0.dp
) {
Text(
modifier = Modifier.padding(16.dp),
text = "text",
style = MaterialTheme.typography.body1.secondary
)
}
if (isKeyboardOpen == Keyboard.Closed) {
Spacer(modifier = Modifier.weight(1f))
CloseAccountButton(state, onEvent, isKeyboardOpen) {
buttonHeight = it
}
} else {
Spacer(modifier = Modifier.size(buttonHeight))
}
}
if (isKeyboardOpen == Keyboard.Opened) {
CloseAccountButton(state, onEvent, isKeyboardOpen) {
buttonHeight = it
}
}
And here is how I get keyboard state:
enum class Keyboard {
Opened, Closed
}
#Composable
fun keyboardAsState(): State<Keyboard> {
val keyboardState = remember { mutableStateOf(Keyboard.Closed) }
val view = LocalView.current
DisposableEffect(view) {
val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
val rect = Rect()
view.getWindowVisibleDisplayFrame(rect)
val screenHeight = view.rootView.height
val keypadHeight = screenHeight - rect.bottom
keyboardState.value = if (keypadHeight > screenHeight * 0.15) {
Keyboard.Opened
} else {
Keyboard.Closed
}
}
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
onDispose {
view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener)
}
}
return keyboardState
}
As I said on first opening of the screen everything is fine but when coming back from background neither scrolling to position nor raacting to keyboard visibility is working. I don't have much experience with compose, what is wrong here?
I want to have a horizontal dot indicator that has color transition between two dots that is scrolling and also dot's size transition while scrolling
I need to show only limited dots for a huge amount of items.
In view system, we used this library https://github.com/Tinkoff/ScrollingPagerIndicator, which is very smooth and has a very nice color and size transition effects.
I tried to implement it with scroll state rememberLazyListState(), but it is more complex than I thought.
Do you know any solution in Jetpack Compose?
Is it possible to use the current library with AndroidView? Because it needs XML view, recycler view and viewpager, I am wondering how is it possible to use it with AndroidView?
I made a sample looks similar, logic for scaling is raw but it looks similar.
#OptIn(ExperimentalPagerApi::class)
#Composable
fun PagerIndicator(
modifier: Modifier = Modifier,
pagerState: PagerState,
indicatorCount: Int = 5,
indicatorSize: Dp = 16.dp,
indicatorShape: Shape = CircleShape,
space: Dp = 8.dp,
activeColor: Color = Color(0xffEC407A),
inActiveColor: Color = Color.LightGray,
orientation: IndicatorOrientation = IndicatorOrientation.Horizontal,
onClick: ((Int) -> Unit)? = null
) {
val listState = rememberLazyListState()
val totalWidth: Dp = indicatorSize * indicatorCount + space * (indicatorCount - 1)
val widthInPx = LocalDensity.current.run { indicatorSize.toPx() }
val currentItem by remember {
derivedStateOf {
pagerState.currentPage
}
}
val itemCount = pagerState.pageCount
LaunchedEffect(key1 = currentItem) {
val viewportSize = listState.layoutInfo.viewportSize
if (orientation == IndicatorOrientation.Horizontal) {
listState.animateScrollToItem(
currentItem,
(widthInPx / 2 - viewportSize.width / 2).toInt()
)
} else {
listState.animateScrollToItem(
currentItem,
(widthInPx / 2 - viewportSize.height / 2).toInt()
)
}
}
if (orientation == IndicatorOrientation.Horizontal) {
LazyRow(
modifier = modifier.width(totalWidth),
state = listState,
contentPadding = PaddingValues(vertical = space),
horizontalArrangement = Arrangement.spacedBy(space),
userScrollEnabled = false
) {
indicatorItems(
itemCount,
currentItem,
indicatorCount,
indicatorShape,
activeColor,
inActiveColor,
indicatorSize,
onClick
)
}
} else {
LazyColumn(
modifier = modifier.height(totalWidth),
state = listState,
contentPadding = PaddingValues(horizontal = space),
verticalArrangement = Arrangement.spacedBy(space),
userScrollEnabled = false
) {
indicatorItems(
itemCount,
currentItem,
indicatorCount,
indicatorShape,
activeColor,
inActiveColor,
indicatorSize,
onClick
)
}
}
}
private fun LazyListScope.indicatorItems(
itemCount: Int,
currentItem: Int,
indicatorCount: Int,
indicatorShape: Shape,
activeColor: Color,
inActiveColor: Color,
indicatorSize: Dp,
onClick: ((Int) -> Unit)?
) {
items(itemCount) { index ->
val isSelected = (index == currentItem)
// Index of item in center when odd number of indicators are set
// for 5 indicators this is 2nd indicator place
val centerItemIndex = indicatorCount / 2
val right1 =
(currentItem < centerItemIndex &&
index >= indicatorCount - 1)
val right2 =
(currentItem >= centerItemIndex &&
index >= currentItem + centerItemIndex &&
index < itemCount - centerItemIndex + 1)
val isRightEdgeItem = right1 || right2
// Check if this item's distance to center item is smaller than half size of
// the indicator count when current indicator at the center or
// when we reach the end of list. End of the list only one item is on edge
// with 10 items and 7 indicators
// 7-3= 4th item can be the first valid left edge item and
val isLeftEdgeItem =
index <= currentItem - centerItemIndex &&
currentItem > centerItemIndex &&
index < itemCount - indicatorCount + 1
Box(
modifier = Modifier
.graphicsLayer {
val scale = if (isSelected) {
1f
} else if (isLeftEdgeItem || isRightEdgeItem) {
.5f
} else {
.8f
}
scaleX = scale
scaleY = scale
}
.clip(indicatorShape)
.size(indicatorSize)
.background(
if (isSelected) activeColor else inActiveColor,
indicatorShape
)
.then(
if (onClick != null) {
Modifier
.clickable {
onClick.invoke(index)
}
} else Modifier
)
)
}
}
enum class IndicatorOrientation {
Horizontal, Vertical
}
Usage
#Composable
private fun PagerIndicatorSample() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.height(40.dp))
val pagerState1 = rememberPagerState(initialPage = 0)
val coroutineScope = rememberCoroutineScope()
PagerIndicator(pagerState = pagerState1) {
coroutineScope.launch {
pagerState1.scrollToPage(it)
}
}
HorizontalPager(
count = 10,
state = pagerState1,
) {
Box(
modifier = Modifier
.padding(10.dp)
.shadow(1.dp, RoundedCornerShape(8.dp))
.background(Color.White)
.fillMaxWidth()
.height(200.dp),
contentAlignment = Alignment.Center
) {
Text(
"Text $it",
fontSize = 40.sp,
color = Color.Gray
)
}
}
val pagerState2 = rememberPagerState(initialPage = 0)
PagerIndicator(
pagerState = pagerState2,
indicatorSize = 24.dp,
indicatorCount = 7,
activeColor = Color(0xffFFC107),
inActiveColor = Color(0xffFFECB3),
indicatorShape = CutCornerShape(10.dp)
)
HorizontalPager(
count = 10,
state = pagerState2,
) {
Box(
modifier = Modifier
.padding(10.dp)
.shadow(1.dp, RoundedCornerShape(8.dp))
.background(Color.White)
.fillMaxWidth()
.height(200.dp),
contentAlignment = Alignment.Center
) {
Text(
"Text $it",
fontSize = 40.sp,
color = Color.Gray
)
}
}
Row(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
val pagerState3 = rememberPagerState(initialPage = 0)
Spacer(modifier = Modifier.width(10.dp))
PagerIndicator(
pagerState = pagerState3,
orientation = IndicatorOrientation.Vertical
)
Spacer(modifier = Modifier.width(20.dp))
VerticalPager(
count = 10,
state = pagerState3,
) {
Box(
modifier = Modifier
.padding(10.dp)
.shadow(1.dp, RoundedCornerShape(8.dp))
.background(Color.White)
.fillMaxWidth()
.height(200.dp),
contentAlignment = Alignment.Center
) {
Text(
"Text $it",
fontSize = 40.sp,
color = Color.Gray
)
}
}
}
}
}
Need to convert from
listState.animateScrollToItem()
to
listState.animateScrollBy()
for smooth indicator change and moving with offset change from Pager.
and do some more methodical scale and color and offset calculating this one is only a temporary solution.
I could integrate this library https://github.com/Tinkoff/ScrollingPagerIndicator easily with compose.
#Composable
fun DotIndicator(scrollState: LazyListState, modifier: Modifier = Modifier) {
AndroidViewBinding(
modifier = modifier,
factory = DotIndicatorBinding::inflate,
) {
dotIndicator.setDotCount(scrollState.layoutInfo.totalItemsCount)
dotIndicator.setCurrentPosition(scrollState.firstVisibleItemIndex)
scrollState.layoutInfo.visibleItemsInfo.firstOrNull()?.size?.let { firstItemSize ->
val firstItemOffset = scrollState.firstVisibleItemScrollOffset
val offset = (firstItemOffset.toFloat() / firstItemSize.toFloat()).coerceIn(0f, 1f)
dotIndicator.onPageScrolled(scrollState.firstVisibleItemIndex, offset)
}
}
}
For the integration I had to add XML file as well ->
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data />
<ru.tinkoff.scrollingpagerindicator.ScrollingPagerIndicator
android:id="#+id/dot_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:spi_dotColor="#color/ds_primary_25"
app:spi_dotSelectedColor="?attr/colorPrimary"
app:spi_dotSelectedSize="8dp"
app:spi_dotSize="8dp"
app:spi_dotSpacing="4dp" />
</layout>
And also adding this dependency to your Gradle file ->
api "androidx.compose.ui:ui-viewbinding:1.1.1"
My problem is that i need a tab indicator to match exactly to the text that is above it (from designs):
However, all i managed to do is get something looking like this:
My code:
ScrollableTabRow(
selectedTabIndex = selectedSeason,
backgroundColor = Color.White,
edgePadding = 0.dp,
modifier = Modifier
.padding(vertical = 24.dp)
.height(40.dp),
indicator = { tabPositions ->
TabDefaults.Indicator(
color = Color.Red,
height = 4.dp,
modifier = Modifier
.tabIndicatorOffset(tabPositions[selectedSeason])
)
}
) {
item.seasonList().forEachIndexed { index, contentItem ->
Tab(
modifier = Modifier.padding(bottom = 10.dp),
selected = index == selectedSeason,
onClick = { selectedSeason = index }
)
{
Text(
"Season " + contentItem.seasonNumber(),
color = Color.Black,
style = styles.seasonBarTextStyle(index == selectedSeason)
)
}
}
}
}
Also a little bonus question, my code for this screen is inside lazy column, now i need to have this tab row to behave somewhat like a sticky header(when it gets to the top, screen stops scrolling, but i can still scroll the items inside it)
Thanks for your help
I had the same requirement, and came up with a simpler solution. By putting the same horizontal padding on the Tab and on the indicator, the indicator aligns with the tab's content:
ScrollableTabRow(selectedTabIndex = tabIndex,
indicator = { tabPositions ->
Box(
Modifier
.tabIndicatorOffset(tabPositions[tabIndex])
.height(TabRowDefaults.IndicatorHeight)
.padding(end = 20.dp)
.background(color = Color.White)
)
}) {
Tab(modifier = Modifier.padding(end = 20.dp, bottom = 8.dp),
selected = tabIndex == 0, onClick = { tabIndex = 0}) {
Text(text = "Tab 1!")
}
Tab(modifier = Modifier.padding(end = 20.dp, bottom = 8.dp),
selected = tabIndex == 1, onClick = { tabIndex = 1}) {
Text(text = "Tab 2!")
}
}
Have a look at the provided modifier, it internally computes a width value. If you change the Modifier yourself to the code below you can provide a width value.
fun Modifier.ownTabIndicatorOffset(
currentTabPosition: TabPosition,
currentTabWidth: Dp = currentTabPosition.width
): Modifier = composed(
inspectorInfo = debugInspectorInfo {
name = "tabIndicatorOffset"
value = currentTabPosition
}
) {
val indicatorOffset by animateAsState(
targetValue = currentTabPosition.left,
animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing)
)
fillMaxWidth()
.wrapContentSize(Alignment.BottomStart)
.offset(x = indicatorOffset + ((currentTabPosition.width - currentTabWidth) / 2))
.preferredWidth(currentTabWidth)
}
Now to the point of how to get the width of your Text:
Warning: I think it's not the way to do it but I can't figure out a better one atm.
At first, I create a Composable to provide me the width of its contents.
#Composable
fun MeasureWidthOf(setWidth: (Int) -> Unit, content: #Composable () -> Unit) {
Layout(
content = content
) { list: List<Measurable>, constraints: Constraints ->
check(list.size == 1)
val placeable = list.last().measure(constraints)
layout(
width = placeable.width.also(setWidth),
height = placeable.height
) {
placeable.placeRelative(x = 0, y = 0)
}
}
}
Now I can use it in your example (simplified):
// Needed for Android
fun Float.asPxtoDP(density: Float): Dp {
return (this / (density)).dp
}
fun main(args: Array<String>) {
Window(size = IntSize(600, 800)) {
val (selectedSeason, setSelectedSeason) = remember { mutableStateOf(0) }
val seasonsList = mutableListOf(2020, 2021, 2022)
val textWidth = remember { mutableStateListOf(0, 0, 0) }
// Android
val density = AmbientDensity.current.density
ScrollableTabRow(
selectedTabIndex = selectedSeason,
backgroundColor = Color.White,
edgePadding = 0.dp,
modifier = Modifier
.padding(vertical = 24.dp)
.height(40.dp),
indicator = { tabPositions ->
TabDefaults.Indicator(
color = Color.Red,
height = 4.dp,
modifier = Modifier
.ownTabIndicatorOffset(
currentTabPosition = tabPositions[selectedSeason],
// Android:
currentTabWidth = textWidth[selectedSeason].asPxtoDP(density)
// Desktop:
currentTabWidth = textWidth[selectedSeason].dp
)
)
}
) {
seasonsList.forEachIndexed { index, contentItem ->
Tab(
modifier = Modifier.padding(bottom = 10.dp),
selected = index == selectedSeason,
onClick = { setSelectedSeason(index) }
)
{
val text = #Composable {
Text(
text = "Season $contentItem",
color = Color.Black,
textAlign = TextAlign.Center
)
}
if (index == selectedSeason) {
MeasureWidthOf(setWidth = { textWidth[index] = it }) {
text()
}
} else {
text()
}
}
}
}
}
}
Edit (05.01.2021): Simplified Modifier code
Edit (09.01.2021): Fixed density problem on android and tested on Desktop and Android