My problem is, that i can't manage to navigate to another Screen inside a foreach-loop altough it's possible in another Screen to route to the wanted Screen, but not in a foreach loop.
When i click on the button, it says, that he can't find the article with the id, but i checked and the id, which he says he can't find does exist
This is my Code (which produces the error):
Surface(
modifier = Modifier
.fillMaxSize()
)
{
Box()
{
shopping.forEach {
Button(
modifier = Modifier
.offset(it.coordinates.x.dp, it.coordinates.y.dp)
.size(10.dp),
onClick = { navController.navigate(
Screen.ArtikelDetailScreen.route
+ "/${it.id}" })
)
{
Text(
modifier = Modifier
.padding(
top = ProjectTheme.paddings.tiny,
bottom = ProjectTheme.paddings.tiny
),
style = ProjectTheme.typography.h6,
text = it.name
)
}
}
}
}
This is the code to which it should route:
#Composable
fun ArtikelDetailScreen(
id: Long,
navController: NavController,
viewModel: ArtikelViewModel = hiltViewModel()
) {
val artikel = viewModel.shoppingState.value.find { p -> p.id == id }
if(artikel == null) {
Log.e(tag, "Artikel mit der id = $id wurde nicht gefunden")
return
}
viewModel.artikel = artikel
viewModel.setState()
val scrollState = rememberScrollState()
ProjectTheme {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = stringResource(R.string.ArtDetailScreenName))
}
)
}
) {
Column(
modifier = Modifier
.wrapContentWidth()
.padding(all = ProjectTheme.paddings.small)
.verticalScroll(
state = scrollState,
enabled = true,
reverseScrolling = true
)
) {
ArtikelInfos(viewModel)
Button(
modifier = Modifier
.fillMaxWidth()
.padding(top = ProjectTheme.paddings.default),
onClick = {
viewModel.remove(id)
viewModel.add()
navController.navigate(
route = Screen.ShoppingListScreen.route)
{
popUpTo(
route = Screen.ShoppingListScreen.route
)
{
inclusive = true
}
}
}
) {
Text(
modifier = Modifier
.padding(
top = ProjectTheme.paddings.tiny,
bottom = ProjectTheme.paddings.tiny
),
style = ProjectTheme.typography.h6,
text = stringResource(R.string.back)
)
}
}
}
}
}
And this is where it works:
LazyColumn {
items(
viewModel.shoppingState.value
)
{
artikel ->
var artikelRemoved: Artikel?
val dismissState = rememberDismissState(
confirmStateChange =
{
if (it == DismissValue.DismissedToEnd) {
logThread("==>SwipeToDismiss().", "-> Edit")
navController.navigate(
Screen.ArtikelDetailScreen.route
+ "/${artikel.id}"
)
return#rememberDismissState true
}
else if (it == DismissValue.DismissedToStart)
{
logThread("==>SwipeToDismiss().", "-> Delete")
artikelRemoved = artikel
viewModel.remove(artikel.id)
navController.navigate(
Screen.ShoppingListScreen.route
)
return#rememberDismissState true
}
return#rememberDismissState false
}
)
SwipeToDismiss(
state = dismissState,
modifier = Modifier.padding(vertical = 4.dp),
directions = setOf(
DismissDirection.StartToEnd,
DismissDirection.EndToStart
),
dismissThresholds =
{
direction ->
FractionalThreshold(
if (direction == DismissDirection.StartToEnd)
{
0.25f
}
else
{
0.25f
}
)
},
background =
{
val direction =
dismissState.dismissDirection ?: return#SwipeToDismiss
val colorBox by animateColorAsState(
when (dismissState.targetValue)
{
DismissValue.Default -> ProjectTheme.colors.secondary
DismissValue.DismissedToEnd -> Color.Green
DismissValue.DismissedToStart -> Color.Red
}
)
val colorIcon by animateColorAsState(
when (dismissState.targetValue)
{
DismissValue.Default -> Color.Black
DismissValue.DismissedToEnd -> Color.DarkGray
DismissValue.DismissedToStart -> Color.White
}
)
val alignment = when (direction)
{
DismissDirection.StartToEnd -> Alignment.CenterStart
DismissDirection.EndToStart -> Alignment.CenterEnd
}
val icon = when (direction)
{
DismissDirection.StartToEnd -> Icons.Default.Edit
DismissDirection.EndToStart -> Icons.Default.Delete
}
val scale by animateFloatAsState(
if (dismissState.targetValue == DismissValue.Default)
{
1.25f
}
else
{
2.0f
}
)
Box(
Modifier
.fillMaxSize()
.background(colorBox)
.padding(horizontal = 20.dp),
contentAlignment = alignment
) {
Icon(
icon,
contentDescription = "Localized description",
modifier = Modifier
.scale(scale),
tint = colorIcon
)
}
},
dismissContent = {
Column {
ArtikelCard(
artikel = artikel,
elevation = animateDpAsState(
if (dismissState.dismissDirection != null)
{
8.dp
}
else
{
0.dp
}
).value,
) {
// onClick
// THIS WORKS navController.navigate(
Screen.ArtikelDetailScreen.route
+ "/${artikel.id}"
)
}
Divider()
}
}
)
}
}
Related
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(" ")
)
}
}
}
I'm using room database with jetpack compose,upon deleting all items the ui is not recomposing unless i move to another screen and come back again , any help would be appreciated , Thank you
This is my code
fun CheckOutScreen(shopViewModel: ShopViewModel,
navHostController: NavHostController,
list : SnapshotStateList<PurchaseModel> ) {
val firebaseAuth = FirebaseAuth.getInstance()
val databaseReference = FirebaseDatabase.getInstance().reference
val context = LocalContext.current
val totalAmount = remember { mutableStateOf(0.0) }
val count = remember { mutableStateOf(0) }
val isDialogShowing = remember { mutableStateOf(false) }
val isProgressShowing = remember { mutableStateOf(false) }
val isDataSaving = remember { mutableStateOf(false) }
val isMovingToAnotherScreen = remember { mutableStateOf(false)}
list.forEach {
if(count.value < list.size){
totalAmount.value += it.totalPrice
count.value++
}
}
Scaffold{
Column {
Row(modifier = Modifier.fillMaxWidth()) {
Text(text = "Cart", modifier = Modifier
.weight(1f)
.padding(10.dp), color = Color.DarkGray,
style = MaterialTheme.typography.h1, fontSize = 17.sp)
IconButton(onClick = {
isDialogShowing.value = true
}) {
Icon(Icons.Filled.Delete,"")
}
}
if(list.size == 0){
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Text("No Items In The Cart", style = MaterialTheme.typography.h1,
color = Color.DarkGray , fontSize = 17.sp)
}
}
else {
Box(contentAlignment = Alignment.Center){
Column() {
Text(text = "Purchase To : " + list[0].fullName, modifier = Modifier.padding(start = 10.dp,
end = 10.dp, top = 5.dp), fontSize = 17.sp,style = MaterialTheme.typography.body1)
LazyColumn(modifier = Modifier
.weight(1f)
.padding(top = 10.dp)){
items(list.size, key = { item -> item.hashCode()}){ pos ->
val dismissState = rememberDismissState(confirmStateChange = {
if(it == DismissValue.DismissedToStart || it == DismissValue.DismissedToEnd){
shopViewModel.deletePurchase(list[pos].purchaseId!!)
Toast.makeText(context,"Item Successfully Dismissed",Toast.LENGTH_SHORT).show()
}
true
})
if(!isMovingToAnotherScreen.value){
SwipeToDismiss(
state = dismissState,
background = {},
dismissContent = {
Card(elevation = 10.dp, modifier = Modifier
.clip(shape = RoundedCornerShape(6.dp))
.padding(5.dp)) {
Row(modifier = Modifier.fillMaxWidth()) {
Box(contentAlignment = Alignment.Center) {
AsyncImage(model = ImageRequest.Builder(context).data(list[pos].productImage).build(),
contentScale = ContentScale.Crop,
modifier = Modifier
.width(80.dp)
.height(80.dp)
.padding(10.dp),
contentDescription = "")
}
Column(modifier = Modifier.weight(1f)) {
Text(list[pos].title, modifier = Modifier.padding(5.dp), fontWeight = FontWeight.Bold)
Text("Quantity : " + list[pos].totalQuantity)
Text("Due To : " + list[pos].totalPrice)
}
}
}
},
directions = setOf(DismissDirection.StartToEnd,DismissDirection.EndToStart))
}
}
}
Card(modifier = Modifier
.fillMaxWidth()
.clip(shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp))) {
Button(modifier = Modifier.padding(start = 20.dp, end = 20.dp),onClick = {
isProgressShowing.value = true
}) {
Text("Pay ${totalAmount.value}")
}
}
}
}
AnimatedVisibility(visible = isProgressShowing.value) {
CircularProgressIndicator(color = Color.DarkGray)
}
}
}
if(isProgressShowing.value){
val map = hashMapOf<String,Any>()
map["list"] = list
map["uid"] = firebaseAuth.currentUser!!.uid
map["totalPrice"] = totalAmount.value.toString()
val db = databaseReference.child("Purchases")
db.child(firebaseAuth.currentUser!!.uid)
.child(Calendar.getInstance().timeInMillis.toString())
.setValue(map)
.addOnSuccessListener {
isDataSaving.value = true
}
.addOnFailureListener {
}
}
if(isDialogShowing.value){
AlertDialog(
onDismissRequest = { isDialogShowing.value = false },
confirmButton = {
TextButton(onClick = {
shopViewModel.deletePurchases()
isDialogShowing.value = false
Toast.makeText(context,"All items are successfully removed",Toast.LENGTH_SHORT).show()
}) {
Text("Proceed")
}
},
dismissButton = {
TextButton(onClick = { isDialogShowing.value = false }) {
Text("Cancel")
}
},
title = { Text("Removing Items ") },
text = { Text("Do you want to remove items from cart ! ") }
)
}
if(isDataSaving.value){
LaunchedEffect(Unit){
delay(3000)
isMovingToAnotherScreen.value = true
shopViewModel.deletePurchases()
isProgressShowing.value = false
isDataSaving.value = false
navHostController.navigate(AppRouting.Payment.route)
}
}
}
BackHandler {
isMovingToAnotherScreen.value = true
navHostController.popBackStack()
}
}
It's most likely because of not updating value of your State. In you example it's always the same list. You either need to set value with new instance of list or use SnapshotStateList. You can check this answer also.
For some reason the Text components are not exactly aligned vertically as the radio buttons in my dialog. I tried adjusting the padding values in every part but there is still not any effect.
#Composable
fun CommonDialog(
title: String?,
state: MutableState<Boolean>,
content: #Composable (() -> Unit)? = null
) {
AlertDialog(
onDismissRequest = {
state.value = false
},
title = title?.let {
{
Column(
Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(5.dp)
) {
Text(text = title)
}
}
},
text = content,
confirmButton = {
TextButton(onClick = { state.value = false }) {
Text(stringResource(id = R.string.button_cancel))
}
}, modifier = Modifier.padding(vertical = 5.dp)
)
}
#Composable
fun AlertSingleChoiceView(state: MutableState<Boolean>) {
CommonDialog(title = stringResource(id = R.string.theme), state = state) {
SingleChoiceView()
}
}
#Composable
fun SingleChoiceView() {
val radioOptions = listOf(
stringResource(id = R.string.straight),
stringResource(id = R.string.curly),
stringResource(id = R.string.wavy))
val (selectedOption, onOptionSelected) = remember { mutableStateOf(radioOptions[2]) }
Column(
Modifier.fillMaxWidth()
) {
radioOptions.forEach { text ->
Row(
Modifier
.fillMaxWidth()
.selectable(
selected = (text == selectedOption),
onClick = {
onOptionSelected(text)
}
)
.padding(vertical = 5.dp)
) {
RadioButton(
selected = (text == selectedOption),
onClick = { onOptionSelected(text) }
)
Text(
text = text
)
}
}
}
}
Current result
You just need to set the verticalAlignment param for the Row.
Row(
Modifier
.fillMaxWidth()
.selectable(
selected = (text == selectedOption),
onClick = {
onOptionSelected(text)
}
)
.padding(vertical = 5.dp),
verticalAlignment = Alignment.CenterVertically // <<<< THIS
) {
...
}
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)
This is my Modal drawer when open
Code to implement it
#ExperimentalMaterialApi
#Composable
private fun TutorialContent() {
ModalDrawerComponent()
}
#ExperimentalMaterialApi
#Composable
private fun ModalDrawerComponent() {
val drawerState = rememberDrawerState(DrawerValue.Closed)
val coroutineScope = rememberCoroutineScope()
val openDrawer: () -> Unit = { coroutineScope.launch { drawerState.open() } }
val closeDrawer: () -> Unit = { coroutineScope.launch { drawerState.close() } }
var selectedIndex by remember { mutableStateOf(0) }
ModalDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerContentHeader()
Divider()
ModelDrawerContentBody(
selectedIndex,
onSelected = {
selectedIndex = it
},
closeDrawer = closeDrawer
)
},
content = {
Column(modifier = Modifier.fillMaxSize()) {
ModalDrawerTopAppBar(openDrawer)
ModalContent(openDrawer)
}
}
)
}
#Composable
fun ModalDrawerTopAppBar(openDrawer: () -> Unit) {
TopAppBar(
title = {
Text("ModalDrawer")
},
navigationIcon = {
IconButton(onClick = openDrawer) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = null
)
}
},
actions = {}
)
}
#Composable
fun ModalDrawerContentHeader() {
Column(
modifier = Modifier
.fillMaxWidth()
.height(180.dp)
.padding(20.dp)
) {
Image(
modifier = Modifier
.size(60.dp)
.clip(CircleShape),
painter = painterResource(id = R.drawable.avatar_1_raster),
contentDescription = null
)
Spacer(modifier = Modifier.weight(1f))
Text(text = "Android", fontWeight = FontWeight.Bold, fontSize = 22.sp)
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(text = "android#android.com")
Spacer(modifier = Modifier.weight(1f))
Icon(imageVector = Icons.Filled.ArrowDropDown, contentDescription = null)
}
}
}
}
#Composable
fun ModelDrawerContentBody(
selectedIndex: Int,
onSelected: (Int) -> Unit,
closeDrawer: () -> Unit
) {
Column(modifier = Modifier.fillMaxWidth()) {
modalDrawerList.forEachIndexed { index, pair ->
val label = pair.first
val imageVector = pair.second
DrawerButton(
icon = imageVector,
label = label,
isSelected = selectedIndex == index,
action = {
onSelected(index)
}
)
}
}
}
#ExperimentalMaterialApi
#Composable
fun ModalContent(openDrawer: () -> Unit) {
LazyColumn {
items(userList) { item: String ->
ListItem(
modifier = Modifier.clickable {
openDrawer()
},
icon = {
Image(
modifier = Modifier
.size(40.dp)
.clip(CircleShape),
painter = painterResource(id = R.drawable.avatar_1_raster),
contentDescription = null
)
},
secondaryText = {
Text(text = "Secondary text")
}
) {
Text(text = item, fontSize = 18.sp)
}
}
}
}
val modalDrawerList = listOf(
Pair("My Files", Icons.Filled.Folder),
Pair("Shared with Me", Icons.Filled.People),
Pair("Starred", Icons.Filled.Star),
Pair("Recent", Icons.Filled.AccessTime),
Pair("Offline", Icons.Filled.OfflineShare),
Pair("Uploads", Icons.Filled.Upload),
Pair("Backups", Icons.Filled.CloudUpload),
)
What i actually desire is to have transparent status over the drawer as in the image below