How to focus a invisible item in lazyColumn on jetpackCompose - android

I want to scroll the lazyColumn when its child get focused.
I use rememberLazyListState's scrollToItem method to scroll the lazyColumn.
but when a item at top been scrolled out, it can't be focused again.
My sample code:
#Composable
fun ScrollableList(modifier: Modifier = Modifier) {
val scrollState = rememberLazyListState()
val scope = rememberCoroutineScope()
LazyColumn(
state = scrollState,
modifier = Modifier.padding(5.dp),
verticalArrangement = Arrangement.spacedBy(5.dp)
) {
itemsIndexed(items = (0..10).toList()) { index, item ->
FocusableBox(
title = "ScrollBox-$index",
onFocused = { focused ->
scope.launch {
if (focused) {
scrollState.scrollToItem(index)
}
}
})
}
}
}
#Composable
fun FocusableBox(
title: String,
onFocused: (Boolean) -> Unit = {},
requester: FocusRequester = FocusRequester(),
modifier: Modifier = Modifier
) {
var boxColor by remember { mutableStateOf(Color.White) }
var focused by remember { mutableStateOf(false) }
Box(
Modifier
.focusRequester(requester)
.onFocusChanged {
boxColor = if (it.isFocused) Color.Green else Color.Gray
focused = it.isFocused
onFocused(it.isFocused)
}
.focusable()
.background(boxColor)
.zIndex(if (focused) 1f else 0f)
) {
Text(
text = title,
modifier = Modifier.padding(30.dp),
color = Color.White,
style = MaterialTheme.typography.subtitle2
)
}
}

add
val scrollState = rememberLazyListState()
then after child been focused, use scrollState.layoutInfo.visibleItemsInfo to determine scroll up or scroll down.
#Composable
fun ScrollableList(modifier: Modifier = Modifier) {
val scrollState = rememberLazyListState()
val scope = rememberCoroutineScope()
LazyColumn(
state = scrollState,
modifier = Modifier.padding(5.dp),
verticalArrangement = Arrangement.spacedBy(5.dp)
) {
itemsIndexed(items = (0..10).toList()) { index, item ->
FocusableBox(
title = "ScrollBox-$index",
onFocused = { focused ->
scope.launch {
if (focused) {
val visibleItemsInfo = scrollState.layoutInfo.visibleItemsInfo
val visibleSet = visibleItemsInfo.map { it.index }.toSet()
if (index == visibleItemsInfo.last().index) {
scrollState.scrollToItem(index)
} else if (visibleSet.contains(index) && index != 0) {
scrollState.scrollToItem(index - 1)
}
}
}
})
}
}
}

Related

Problems with the Drawer on material3

It's not recognizing the parameter and invocations related to the drawer
#Composable
fun PantallaPrincipal(userViewModel: UserViewModel) {
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(initialValue = DrawerValue.Closed))
val scope= rememberCoroutineScope()
Scaffold(scaffoldState = scaffoldState,
topBar= { TopBar(scope,scaffoldState=scaffoldState)},
bottomBar= { BottomBar(navController=navController)},
drawerContent = { Drawer(scope=scope,scaffoldState=scaffoldState,navController=navController)},
floatingActionButton = {FloatingBottom(navController=navController)}
){
Navigation(navController = navController, userViewModel = userViewModel)
}
}
#Composable
fun DrawerItem (item: navigationDrawer, selected:Boolean, onclick:(navigationDrawer)->Unit) {
val itemBackground=if(selected) Color.Gray else Color.Transparent
Row(modifier = Modifier
.fillMaxWidth()
.height(45.dp)
.background(itemBackground)
.padding(10.dp)
.clickable { onclick(item) },
verticalAlignment = Alignment.CenterVertically
) {
Image(
imageVector = item.icono,
contentDescription = item.texto,
colorFilter = ColorFilter.tint(Color.Black),
modifier = Modifier
.height(35.dp)
.width(35.dp)
)
Spacer(modifier = Modifier.width(7.dp))
Text(text = item.texto,
fontSize = 18.sp,
color = Color.Black
)
}
}
#Composable
fun Drawer(scope: CoroutineScope, scaffoldState: ScaffoldState, navController: NavHostController) {
val listItems = listOf<navigationDrawer>(navigationDrawer.Inicio, navigationDrawer.Favoritos, navigationDrawer.Perfil)
val rutaActual = navController.currentBackStackEntry?.destination?.route
Column(modifier = Modifier
.background(Color.White)
){
Image(imageVector = Icons.Default.Face,
contentDescription = "logo",
modifier = Modifier
.background(Color.White)
.height(100.dp)
.fillMaxWidth()
.padding(10.dp)
)
Spacer(modifier = Modifier
.fillMaxWidth()
.height(5.dp)
)
listItems.forEach{
DrawerItem(item = it,
selected = (rutaActual==it.ruta),
onclick = {navController.navigate(it.ruta)
scope.launch {scaffoldState.drawerState.close()}
})
}
}
}
Important: You must use scaffold as ModalNavigationDrawer content.
Material 3 Example - Navigation Drawer with scaffold in compose
val scope = rememberCoroutineScope()
val drawerState = rememberDrawerState(DrawerValue.Closed)
val items = listOf(Icons.Default.Close, Icons.Default.Clear, Icons.Default.Call)
val selectedItem = remember { mutableStateOf(items[0]) }
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Spacer(Modifier.height(12.dp))
items.forEach { item ->
NavigationDrawerItem(
icon = { Icon(item, contentDescription = null) },
label = { Text(item.name) },
selected = item == selectedItem.value,
onClick = {
scope.launch { drawerState.close() }
selectedItem.value = item
},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
}
}
},
content = {
Scaffold(
topBar = { TopBar(preesOnDrawerIcon = { scope.launch { drawerState.open() } }) },
bottomBar = {},
snackbarHost = {},
content = {},
)
}
)
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun TopBar(pressOnDrawer: () -> Unit){
...
}
In Material 3 it's called ModalNavigationDrawer. You can use it using a ModalDrawerSheet and ModalDrawerItems like in this example taken from the docs:
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Spacer(Modifier.height(12.dp))
items.forEach { item ->
NavigationDrawerItem(
icon = { Icon(item, contentDescription = null) },
label = { Text(item.name) },
selected = item == selectedItem.value,
onClick = {
scope.launch { drawerState.close() }
selectedItem.value = item
},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
}
}
},

How to connect reusable button with text field in Jetpack Compose

I'm trying to hide a reusable floating action button (on click - then show it again when the keyboard is not visible) and request focus for a text field I made but I'm not sure what I'm doing wrong. I used focusRequester.requestFocus() and AnimatedVisibility but nothing happens.
#file:OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MySearchViewDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainScreen(showSearchButton = true)
}
}
}
}
sealed interface Shoes { ... }
// Reusable component
#Composable
fun MyReusableScaffold(scaffoldTitle: String,
scaffoldFab: #Composable () -> Unit,
scaffoldContent: #Composable (contentPadding: PaddingValues) -> Unit) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
LargeTopAppBar(
title = { Text(text = scaffoldTitle) },
scrollBehavior = scrollBehavior
)
},
floatingActionButton = scaffoldFab,
content = { contentPadding ->
scaffoldContent(contentPadding = contentPadding)
}
)
}
// Reusable component
#Composable
fun MyExtendedFAB(expandedFab: Boolean,
onFabClick: () -> Unit
) {
ExtendedFloatingActionButton(
text = { Text(text = "Search") },
icon = { Icon(Icons.Rounded.Search, "") },
onClick = onFabClick,
expanded = expandedFab
)
}
// Reusable component
#Composable
fun <T> MyLazyColumn(modifier: Modifier,
lazyItems: Array<T>,
onClickItem: (T) -> Unit,
item: #Composable RowScope.(item: T) -> Unit,
listState: LazyListState
) {
var mText by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
Row {
LazyColumn(
modifier = modifier,
state = listState
) {
item {
if (showNotice){
Text(
text = textNotice,
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
fontStyle = FontStyle.Italic,
style = MaterialTheme.typography.bodyLarge
)
}
}
item {
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
value = mText,
onValueChange = {
mText = it
},
label = {
Text(text = "Search")
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(
onSearch = {
keyboardController?.hide()
focusManager.clearFocus()
}),
)
}
items(lazyItems) { choice ->
Row(modifier = Modifier
.fillMaxWidth()
.clickable {
onClickItem(choice)
}) {
item(choice)
}
}
}
}
}
#Composable
fun MainScreen(
showSearchButton: Boolean
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val listState = rememberLazyListState()
val expandedFab by remember {
derivedStateOf {
listState.firstVisibleItemIndex == 0
}
}
val focusRequester = remember { FocusRequester() }
MyReusableScaffold(
scaffoldTitle = "Shoes",
scaffoldIcon = {},
scaffoldFab = {
AnimatedVisibility(visible = showSearchButton) {
MyExtendedFAB(expandedFab) {
focusRequester.requestFocus()
}
}
},
scaffoldContent = { HomeScreenContent(listState = listState, contentPadding = it) }
)
}
#Composable
fun HomeScreenContent(
modifier: Modifier = Modifier,
listState: LazyListState,
contentPadding: PaddingValues = PaddingValues()
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val shoeItems = remember { arrayOf( ... ) }
MyLazyColumn(
modifier = Modifier.padding(contentPadding),
lazyItems = shoeItems,
onClickItem = {},
item = { ReusableTitleSubtitle(it) },
listState = listState
)
}
}

Kotlin - How to change the background of Box with every LazyRow item

I am trying to make a new app with Jetpack Compose and in this app will be a LazyRow with different items in a Box. With every item that is fully visible the background of the box should be changed from the color saved in the data model.
This is the code:
fun MainScreen(navHostController: NavHostController) {
var bgColor: Color = Color.Red
val state = rememberLazyListState()
val fullyVisibleIndices: List<Int> by remember {
derivedStateOf {
val layoutInfo = state.layoutInfo
val visibleItemsInfo = layoutInfo.visibleItemsInfo
if (visibleItemsInfo.isEmpty()) {
emptyList()
} else {
val fullyVisibleItemsInfo = visibleItemsInfo.toMutableList()
val lastItem = fullyVisibleItemsInfo.last()
val viewportHeight = layoutInfo.viewportEndOffset + layoutInfo.viewportStartOffset
if (lastItem.offset + lastItem.size > viewportHeight) {
fullyVisibleItemsInfo.removeLast()
}
val firstItemIfLeft = fullyVisibleItemsInfo.firstOrNull()
if (firstItemIfLeft != null && firstItemIfLeft.offset < layoutInfo.viewportStartOffset) {
fullyVisibleItemsInfo.removeFirst()
}
fullyVisibleItemsInfo.map { it.index }
}
}
}
addGrunges()
Box(
modifier = Modifier
.fillMaxSize()
.background(bgColor)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 100.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Devlet seçin", style = MaterialTheme.typography.h3
)
LazyRow(
state = state,
modifier = Modifier
.fillMaxWidth()
.padding(top = 100.dp, bottom = 250.dp)
) {
itemsIndexed(grungesList) { index, items ->
bgColor = if (fullyVisibleIndices.contains(index)) items.color else Color.Red
ListColumn(model = items)
}
}
}
}
}
Thanks for your help!
Only fully visible items? Naive implementation
const val contentPadding = 10
#Composable
fun Greeting(models: List<Int> = remember { (0..20).toList() }) {
val state = rememberLazyListState()
val highlightState = remember { HighlightState() }
val hightlightIndices by remember {
derivedStateOf {
val layoutInfo = state.layoutInfo
val visibleItemsInfo = layoutInfo.visibleItemsInfo
val viewportWidth = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset
if (visibleItemsInfo.isNotEmpty()) {
val indicies = ArrayList<Int>(visibleItemsInfo.size)
for (ix in visibleItemsInfo.indices) {
val item = visibleItemsInfo[ix]
when (ix) {
0 -> if (item.offset + contentPadding >= 0) indicies.add(item.index)
visibleItemsInfo.size - 1 -> {
if (item.offset + item.size + contentPadding < viewportWidth) indicies.add(item.index)
}
else -> indicies.add(item.index)
}
}
highlightState.update(indicies)
}
highlightState.indicies
}
}
LazyRow(
modifier = Modifier.fillMaxWidth(),
//.mouseScroll(state), // for desktop only
state = state,
contentPadding = PaddingValues(contentPadding.dp, 0.dp),
horizontalArrangement = Arrangement.spacedBy(30.dp)
) {
itemsIndexed(models) { index, model ->
MyListItem(model, hightlightIndices.contains(index))
}
}
}
#Composable
fun MyListItem(value: Int, highlight: Boolean) {
Box(
modifier = Modifier.height(100.dp)
.aspectRatio(1f)
.background(if (highlight) Color.Red else Color.Yellow),
contentAlignment = Alignment.Center
) {
Text(value.toString())
}
}
fun <T : Comparable<T>> Iterable<T>.compareTo(other: Iterable<T>): Int {
val otherI = other.iterator()
for (e in this) {
if (!otherI.hasNext()) return 1 // other has run out of elements, so `this` is larger
val c = e.compareTo(otherI.next())
if (c != 0) return c // found a position with a difference
}
if (otherI.hasNext()) return -1 // `this` has run out of elements, but other has some more, so other is larger
return 0 // they're the same
}
class HighlightState {
var indicies: List<Int> = emptyList()
private set
fun update(newIndicies: List<Int>) {
if (indicies.compareTo(newIndicies) != 0)
indicies = newIndicies
}
}
result

Jetpack Compose - Cannot animate Scaffold's bottom bar's height

I need to reveal some floating action buttons at the bottom of the screen when some other buttons, in a scrollable list, are no longer visible on the screen. Naturally, I feel like using LazyColumn, AnimatedVisibility and Scaffold's bottom bar for this (I need the snackbar and the screen's content be above the floating action buttons). But the animation doesn't work as expected. As the floating buttons appear/dissapear, the bottom bar's height is not animated (but I think it could actually be that the content padding isn't animated).
The result I'm getting:
The code:
val lazyListState = rememberLazyListState()
val isFloatingButtonVisible by derivedStateOf {
lazyListState.firstVisibleItemIndex >= 2
}
Scaffold(
bottomBar = {
AnimatedVisibility(
visible = isFloatingButtonVisible,
enter = slideInVertically { height -> height },
exit = slideOutVertically { height -> height }
) {
Row(
modifier = Modifier.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
repeat(2) {
Button(
modifier = Modifier
.height(48.dp)
.weight(1f),
onClick = {}
) {
Text(text = "Something")
}
}
}
}
}
) { contentPadding ->
LazyColumn(
modifier = Modifier.padding(contentPadding),
state = lazyListState,
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
Text(
text = LoremIpsum(50).values.joinToString(" ")
)
}
item {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
repeat(2) {
Button(onClick = {}) {
Text(text = "Bla Bla")
}
}
}
}
item {
Text(
text = LoremIpsum(700).values.joinToString(" ")
)
}
}
}
I don't think it's possible to do this with AnimatedVisibility. I got it like this
#Composable
fun Test() {
val lazyListState = rememberLazyListState()
val isFloatingButtonVisible by remember {
derivedStateOf {
lazyListState.firstVisibleItemIndex >= 1
}
}
var barHeight by remember { mutableStateOf(0.dp) }
var listSizeDelta by remember { mutableStateOf(0.dp) }
val bottomBarOffset = remember { Animatable(barHeight, Dp.VectorConverter) }
LaunchedEffect(isFloatingButtonVisible) {
if (isFloatingButtonVisible) {
bottomBarOffset.animateTo(barHeight)
listSizeDelta = barHeight
} else {
listSizeDelta = 0.dp
bottomBarOffset.animateTo(0.dp)
}
}
val density = LocalDensity.current
BoxWithConstraints(Modifier.fillMaxSize()) {
LazyColumn(
modifier = Modifier
.size(width = maxWidth, height = maxHeight - listSizeDelta)
.background(Color.Yellow),
state = lazyListState,
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
repeat(15) {
item {
Text(
text = LoremIpsum(50).values.joinToString(" ")
)
}
}
}
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.graphicsLayer {
translationY =
density.run { (-bottomBarOffset.value + barHeight).toPx() }
}
.background(Color.Green)
.onSizeChanged {
barHeight = density.run { it.height.toDp()}
}
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
repeat(2) {
Button(
modifier = Modifier
.height(48.dp)
.weight(1f),
onClick = {}
) {
Text(text = "Something")
}
}
}
}
}
The content padding in lazycolumn modifier breaks the animation;
LazyColumn(
modifier = Modifier.padding(contentPadding) // -> Delete this.
)
For the same view;
Row(
modifier = Modifier
.background(Color.White) //-> Add this.
.padding(8.dp)
)
Got the right result using AnimatedContent instead of AnimatedVisibility.
val lazyListState = rememberLazyListState()
val isFloatingButtonVisible by derivedStateOf {
lazyListState.firstVisibleItemIndex >= 2
}
Scaffold(
bottomBar = {
AnimatedContent(
targetState = isFloatingButtonVisible,
transitionSpec = {
slideInVertically { height -> height } with
slideOutVertically { height -> height }
}
) { isVisible ->
if (isVisible) {
Row(
modifier = Modifier
.border(1.dp, Color.Red)
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
repeat(2) {
Button(
modifier = Modifier
.height(48.dp)
.weight(1f),
onClick = {}
) {
Text(text = "Something")
}
}
}
} else {
Box(modifier = Modifier.fillMaxWidth())
}
}
}
) { contentPadding ->
LazyColumn(
modifier = Modifier.padding(contentPadding),
state = lazyListState,
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
Text(
text = LoremIpsum(50).values.joinToString(" ")
)
}
item {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
repeat(2) {
Button(onClick = {}) {
Text(text = "Bla Bla")
}
}
}
}
item {
Text(
text = LoremIpsum(700).values.joinToString(" ")
)
}
}
}

ConcurrentModificationException during mutableStateListOf reverse

itemList.reverse()
itemList is mutableStateListOf() object inside viewModel, above line throws below given exception:
java.util.ConcurrentModificationException
at androidx.compose.runtime.snapshots.StateListIterator.validateModification(SnapshotStateList.kt:278)
at androidx.compose.runtime.snapshots.StateListIterator.set(SnapshotStateList.kt:271)
at java.util.Collections.reverse(Collections.java:435)
at kotlin.collections.CollectionsKt___CollectionsJvmKt.reverse(_CollectionsJvm.kt:43)
at in.rachika.composetest2.Tests.LazyColumnHeaderTest$MainScreenWithChildList$1$2.invoke(LazyColumnHeaderTest.kt:114)
at in.rachika.composetest2.Tests.LazyColumnHeaderTest$MainScreenWithChildList$1$2.invoke(LazyColumnHeaderTest.kt:111)
I am unable to figure out how to reverse or shuffle a mutableStateListOf() Object
reverse() works well in isolated situation but my LazyColumn has stickyHeader() and SwipeToDismiss(), don't know may be that is creating problem.
Model
data class TestModel(
val isHeader: Boolean,
val UniqueKey: UUID,
val GroupId: UUID,
val GroupName: String,
val ItemName: String,
val children: MutableList<TestModel>,
var isExpanded: Boolean = false)
ViewModel
class TestViewModel: ViewModel() {
var itemList = mutableStateListOf<TestModel>()
init {
viewModelScope.launch(Dispatchers.IO){
loadList()
}
}
private fun loadList() {
for(i in 0..20){
val groupName = "${i + 1}. STICKY HEADER #"
val groupUUID = UUID.randomUUID()
val childList = mutableListOf<TestModel>()
for(t in 0..Random.nextInt(10, 20)){
childList.add(TestModel(
isHeader = false,
UniqueKey = UUID.randomUUID(),
GroupId = groupUUID,
GroupName = groupName,
ItemName = "${t + 1}. This is an CHILD ITEM... #${i + 1} - ${Random.nextInt(1001, 5001)}",
children = ArrayList()
)
)
}
viewModelScope.launch(Dispatchers.Main){
itemList.add(TestModel(
isHeader = true,
UniqueKey = UUID.randomUUID(),
GroupId = groupUUID,
GroupName = groupName,
ItemName = "",
children = childList
))
}
}
}
fun addChildren(testModel: TestModel, onCompleted: (startIndex: Int) -> Unit){
if(testModel.children.count() > 0){
var index = itemList.indexOf(testModel)
testModel.children.forEach { tItem ->
itemList.add(index + 1, tItem)
index++
}
testModel.apply {
isExpanded = true
children.clear()
}
onCompleted(index)
}
}
fun removeChildren(testModel: TestModel, onCompleted: (startIndex: Int) -> Unit){
val startIndex = itemList.indexOf(testModel) + 1
while (startIndex < itemList.size && !itemList[startIndex].isHeader){
testModel.children.add(itemList.removeAt(startIndex))
}
if(testModel.children.count() > 0){
testModel.isExpanded = false
onCompleted(startIndex - 1)
}
}}
Composable functions
#Composable
fun MainScreenWithChildList(testViewModel: TestViewModel = viewModel()) {
val lazyColumnState = rememberLazyListState()
var reverseList by remember { mutableStateOf(false) }
if (reverseList) {
LaunchedEffect(Unit) {
delay(1000)
testViewModel.itemList.reverse()
}
}
Box(modifier = Modifier.fillMaxSize()) {
LazyColumn(
state = lazyColumnState,
modifier = Modifier.fillMaxSize()
) {
testViewModel.itemList.forEach { testModel ->
when (testModel.isHeader) {
true -> {
stickyHeader(key = testModel.UniqueKey) {
HeaderLayout(testModel = testModel) { testModel ->
if (testModel.isExpanded) {
testViewModel.removeChildren(testModel) {}
} else {
testViewModel.addChildren(testModel) {}
}
}
}
}
false -> {
item(key = testModel.UniqueKey) {
val dismissState = rememberDismissState()
if (dismissState.isDismissed(DismissDirection.EndToStart) || dismissState.isDismissed(
DismissDirection.StartToEnd
)
) {
if (dismissState.currentValue != DismissValue.Default) {
LaunchedEffect(Unit) {
dismissState.reset()
}
}
}
SwipeToDismiss(
state = dismissState,
directions = setOf(
DismissDirection.StartToEnd,
DismissDirection.EndToStart
),
dismissThresholds = { direction ->
FractionalThreshold(if (direction == DismissDirection.StartToEnd || direction == DismissDirection.EndToStart) 0.25f else 0.5f)
},
background = { SwipedItemBackground(dismissState = dismissState) },
dismissContent = {
ItemScreen(
modifier = Modifier
.fillMaxWidth()
.animateItemPlacement(animationSpec = tween(600)),
elevation = if (dismissState.dismissDirection != null) 16 else 0,
testModel = testModel
)
}
)
}
}
}
}
}
Button(
onClick = {
reverseList = true
},
modifier = Modifier.align(Alignment.BottomCenter)
) {
Text(text = "Reverse")
}
}
}
#Composable
fun ItemScreen(modifier: Modifier, elevation: Int, testModel: TestModel) {
Card(
modifier = modifier,
//elevation = animateDpAsState(elevation.dp).value
) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
text = testModel.ItemName + " ="
)
}
}
#Composable
fun SwipedItemBackground(dismissState: DismissState) {
val direction = dismissState.dismissDirection ?: return
val color by animateColorAsState(
targetValue = when (dismissState.targetValue) {
DismissValue.Default -> Color.LightGray
DismissValue.DismissedToEnd -> Color.Green
DismissValue.DismissedToStart -> Color.Red
}
)
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.Done
DismissDirection.EndToStart -> Icons.Default.Delete
}
val scale by animateFloatAsState(
if (dismissState.targetValue == DismissValue.Default) 0.75f else 1.5f
)
val alignment = when (direction) {
DismissDirection.StartToEnd -> Alignment.CenterStart
DismissDirection.EndToStart -> Alignment.CenterEnd
}
Box(
modifier = Modifier
.fillMaxSize()
.background(color)
.padding(start = 12.dp, end = 12.dp),
contentAlignment = alignment
) {
Icon(icon, contentDescription = "Icon", modifier = Modifier.scale(scale))
}
}
#Composable
fun HeaderLayout(testModel: TestModel, onExpandClicked: (testModel: TestModel) -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(
color = Color.LightGray,
shape = RoundedCornerShape(8.dp)
)
.padding(horizontal = 32.dp, vertical = 16.dp),
horizontalArrangement = SpaceBetween
) {
Text(text = testModel.GroupName)
TextButton(onClick = { onExpandClicked(testModel) }) {
Text(text = if (testModel.isExpanded) "Collapse" else "Expand")
}
}
}
Above is the complete reproducible code. Please copy paste and try
The reverse seems to work fine when the list size is small, but crashes when the number of items is large. I was able to reproduce this with the following MRE:
val list = remember { (1..100).toList().toMutableStateList() }
LaunchedEffect(Unit) {
delay(1.seconds)
list.reverse()
}
Text(list.toList().toString())
And reported this to compose issue tracker, star it so it can be solved faster.
Until then, you can reverse it manually as follows:
val newItems = list.reversed()
list.clear()
list.addAll(newItems)

Categories

Resources