jetpack compose lazycolumn items not shows deleted only after scroll - android-jetpack-compose

Could you pelase help me?
I want to use swipe to delete and tried a lot of things, but nothing works good. The best way is:
items(listactivity.size) { index ->
val activityItem = listactivity.getOrNull(index)
if (activityItem != null) {
key(activityItem) {
val dismissState = rememberDismissState()
LaunchedEffect(dismissState.currentValue) {
if (dismissState.currentValue == DismissValue.DismissedToStart) {
dashboardViewModel.activityForDelete = activityItem
println("ACT ITEM " + activityItem.activityType?.activityTypeName)
visibleDeleteDialog.value = true
dismissState.snapTo(DismissValue.Default)
}
}
if (visibleDeleteDialog.value) {
BaseAlertDialog(
onExit = {
visibleDeleteDialog.value = false
},
onSuccess = {
removeActivity()
visibleDeleteDialog.value = false
},
disclaimerFirst = R.string.confirm_delete_activity,
disclaimerSecond = R.string.confirm_delete_activity_text,
successName = R.string.delete_session
)
}
SwipeToDismiss(
state = dismissState,
directions = setOf(DismissDirection.EndToStart),
background = {
val direction =
dismissState.dismissDirection ?: return#SwipeToDismiss
val alignment = when (direction) {
DismissDirection.StartToEnd -> Alignment.CenterStart
DismissDirection.EndToStart -> CenterEnd
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Black)
.padding(horizontal = Spacing_12),
contentAlignment = alignment
) {
Text(
text = stringResource(id = R.string.delete),
color = Color.Red,
fontSize = Text_16,
fontWeight = FontWeight.Bold
)
}
},
dismissContent = {
GameCard(activityItem)
}
)
}
}
}
However th lisitem do not update items after delete immidiately (only after scroll i can see what item was deleted).
What am i doing wrong?
I tried SwipeToDismiss and other internet things.

I know google has a bug in this function items() some times,
but please try to replace your function with itemsIndexed and add another parameter
itemsIndexed(listactivity.size) { index , item ->
something like this
// import this at the top
import androidx.compose.foundation.lazy.itemsIndexed
//code
//*
//*
//*
itemsIndexed(listactivity.size) { index , item ->
val activityItem = listactivity.getOrNull(index)
if (activityItem != null) {
key(activityItem) {
val dismissState = rememberDismissState()
LaunchedEffect(dismissState.currentValue) {
if (dismissState.currentValue == DismissValue.DismissedToStart) {
dashboardViewModel.activityForDelete = activityItem
println("ACT ITEM " + activityItem.activityType?.activityTypeName)
visibleDeleteDialog.value = true
dismissState.snapTo(DismissValue.Default)
}
}
if (visibleDeleteDialog.value) {
BaseAlertDialog(
onExit = {
visibleDeleteDialog.value = false
},
onSuccess = {
removeActivity()
visibleDeleteDialog.value = false
},
disclaimerFirst = R.string.confirm_delete_activity,
disclaimerSecond = R.string.confirm_delete_activity_text,
successName = R.string.delete_session
)
}
SwipeToDismiss(
state = dismissState,
directions = setOf(DismissDirection.EndToStart),
background = {
val direction =
dismissState.dismissDirection ?: return#SwipeToDismiss
val alignment = when (direction) {
DismissDirection.StartToEnd -> Alignment.CenterStart
DismissDirection.EndToStart -> CenterEnd
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Black)
.padding(horizontal = Spacing_12),
contentAlignment = alignment
) {
Text(
text = stringResource(id = R.string.delete),
color = Color.Red,
fontSize = Text_16,
fontWeight = FontWeight.Bold
)
}
},
dismissContent = {
GameCard(activityItem)
}
)
}
}
}
I don't have the time to go over all of your code to make more changes that maybe will make it a better fit for your code and needs. but I am sure you are able to do so!
you can see my code when I had a similar problem maybe it will help to solve your problem.
itemsIndexed(celebsSearchList) { index, item ->
if (celebsSearchList.isEmpty()) {
println("no celebs")
} else {
val celebie = item
// celebsSearchList.forEach { celebie ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp, top = 10.dp, bottom = 10.dp),
elevation = 8.dp
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(MaterialTheme.shapes.medium),
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = celebie.FirstName + " " + celebie.LastName,
style = MaterialTheme.typography.h6
)
Button(
onClick = {
CelebForCelebProfile = celebie
navController.navigate(Screen.CelebProfileScreen.route)
}, colors = ButtonDefaults.buttonColors(
backgroundColor = Color.White, contentColor = Color.Gray
)
) {
Text(text = "View Profile")
}
}
}
}
}```
if you like me please upvote me :) help me gain more points on stack over flow and comment if you have any questions.

Related

Smooth scrolling of LazyColumn in desktop compose

I made LazyColumn with vertical scroll bar and it's work good, but when i scroll mouse, column just jumping(not smooth), but when I scroll vert. bar, it's smooth
#ExperimentalFoundationApi
#OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
#Composable
fun App() {
Box(modifier = Modifier.fillMaxSize().padding(10.dp)
) {
val animatedpr = remember { androidx.compose.animation.core.Animatable(initialValue = 0.8f) }
val stateVertical = rememberLazyListState(0)
LaunchedEffect(Unit){animatedpr.animateTo(targetValue = 1f, animationSpec = tween(300, easing = LinearEasing))}
LazyColumn(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, state = stateVertical) {
items(1100) {
OutlinedCard(modifier = Modifier.size(500.dp, 100.dp).padding(20.dp).animateItemPlacement().graphicsLayer(scaleY = animatedpr.value, scaleX = animatedpr.value)) {
}
}
}
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
adapter = rememberScrollbarAdapter(
scrollState = stateVertical
)
)
}
}
better sollution
if(it.changes.first().scrollDelta.y == 1.0f){
scope.launch { stateVertical.animateScrollBy(200.0f) }
}
else{
scope.launch {
scope.launch { stateVertical.animateScrollBy(-200.0f) }
}
}
The problem is that you use a single scroll state for two different scroll views. This is causing the jumping while recomposing.
I fixed it by adding "scrollhandler"(onPointerEvent(PointerEventType.Scroll))
val scope = rememberCoroutineScope() // coroutine for scroling(idk, i know coroutines very bad)
val stateVertical = rememberLazyListState(0)
.....
LazyColumn(modifier = Modifier.fillMaxSize().onPointerEvent(PointerEventType.Scroll){
var currentItem = stateVertical.layoutInfo.visibleItemsInfo[0].index
val itemsToScrolling = stateVertical.layoutInfo.visibleItemsInfo.size/2 // how many items we scrolling
if(it.changes.first().scrollDelta.y == 1.0f){ // scroll down
scope.launch { stateVertical.animateScrollToItem(currentItem+itemsToScrolling) }
}
else{ // scroll up
if(currentItem < itemsToScrolling){currentItem = itemsToScrolling} // because we cannot animate to negative number
scope.launch { stateVertical.animateScrollToItem(currentItem-itemsToScrolling) }
}
}, state = stateVertical){
*items*
}

Jetpack Compose Desktop is there a way to add elevation to window? How can I set minimun size of window?

I'm having trouble going through tutorials of compose desktop.
I have two question regarding the basics of compose desktop.
First, is there a way to add elevation to the entire windows?
Second, is there a way to set the mininum size of the window?
When I click on the edge of the windows, even without any arrows showing, the windows can be resized, but I can't find a way to add constraints on it's minimum size. It will shink until it disappears.
My current preview looks like this.
My code looks like this.
fun main() = application {
val windowState = rememberWindowState()
windowState.size = WindowSize(392.dp, 642.dp)
var isVisible by remember { mutableStateOf(true) }
val trayState = rememberTrayState()
Tray(
state = trayState,
icon = TrayIcon,
hint = "PickMessengerV2",
onAction = {
isVisible = true
},
menu = {
Item(
"Exit",
onClick = {
exitApplication()
}
)
}
)
Window(
onCloseRequest = {
isVisible = false
},
state = windowState,
undecorated = true,
resizable = true,
visible = isVisible
) {
AppTheme(darkTheme = false) {
Row(modifier = Modifier.fillMaxSize()) {
var menuIdx by remember {
mutableStateOf(value = 0)
}
WindowDraggableArea {
Column(
modifier = Modifier.width(67.dp).fillMaxHeight().background(Color(0xFFf3f5f6)),
horizontalAlignment = Alignment.CenterHorizontally
) {
val onClick: (Int) -> Unit = { myIndex ->
menuIdx = myIndex
}
mainMenu(
menuIdx,
0,
listOf("LeftMenu_friend_ON.png", "LeftMenu_friend_OFF.png"),
marginTop = 36,
onClick = onClick
)
mainMenu(
menuIdx,
1,
listOf("LeftMenu_ Orga_ON.png", "LeftMenu_ Orga_OFF.png"),
onClick = onClick
)
mainMenu(
menuIdx,
2,
listOf("LeftMenu_chat_ON.png", "LeftMenu_chat_OFF.png"),
onClick = onClick
)
mainMenu(
menuIdx,
3,
listOf("LeftMenu_letter_OFF_2.png", "LeftMenu_letter_OFF.png"),
onClick = onClick
)
mainMenu(
menuIdx,
4,
listOf("LeftMenu_file_ON.png", "LeftMenu_file_OFF.png"),
onClick = onClick
)
mainMenu(
menuIdx,
5,
listOf("LeftMenu_ Coop_ON.png", "LeftMenu_ Coop_OFF.png"),
onClick = onClick
)
mainMenu(
menuIdx,
6,
listOf("LeftMenu_bnad_ON.png", "LeftMenu_band_OFF.png"),
onClick = onClick
)
}
}
Column(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background)) {
WindowDraggableArea{
Row(
modifier = Modifier.fillMaxWidth().height(31.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.weight(1f))
Box(modifier = Modifier.fillMaxHeight().clickable {
windowState.isMinimized = true
}.padding(start = 6.dp, end = 6.dp), contentAlignment = Alignment.Center) {
NsImage("win_1.png")
}
Box(modifier = Modifier.fillMaxHeight().clickable {
windowState.placement = if(windowState.placement == WindowPlacement.Fullscreen){
WindowPlacement.Floating
}else {
WindowPlacement.Fullscreen
}
}.padding(start = 6.dp, end = 6.dp), contentAlignment = Alignment.Center) {
NsImage("win_2.png")
}
Box(modifier = Modifier.fillMaxHeight().clickable {
isVisible = false
}.padding(start = 6.dp, end = 6.dp), contentAlignment = Alignment.Center) {
NsImage("win_3.png")
}
Spacer(modifier = Modifier.width(6.dp))
}
}
}
}
}
}
}
Here's the solution for minimum window size:
You need to access the AWT API through the FrameWindowScope
fun FrameWindowScope.setMinSize() {
window.minimumSize = Dimension(yourMinWidth, yourMinHeight)
}
And then you just call the setMinSize() in your Window function.

Jetpack Compose Lazy Column is stretching items when end of list is reached

Why my LazyColumn items are being stretched?
class RecipeListFragment: Fragment() {
private val viewModel: RecipeListViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
val recipes = viewModel.recipes.value
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "RecipeList",
style = TextStyle(
fontSize = 21.sp
)
)
Spacer(modifier = Modifier.padding(10.dp))
Button(
onClick = {
findNavController().navigate(R.id.viewRecipe)
}
) {
Text(text = "TO RECIPE FRAGMENT")
}
Spacer(modifier = Modifier.padding(10.dp))
LazyColumn{
items(recipes) { recipe ->
// RecipeCard(recipe = recipe, onClick = {})
Text(
text = recipe.title ?: "None",
style = MaterialTheme.typography.h5
)
}
}
}
}
}
}
}
android 12 preview versions prior to ~august build have this problem.
More info can be found here: https://issuetracker.google.com/issues/208694349
Try on the stable android12 and/or on the real device

Android Jetpack Compose Row's height (IntrinsicSize.Min) is not stretched when children column generate more composables

The following is my code snippet. I pass in editClick that adds a data class object into chargingViewModel.contractSelfPay, which is observed as itemList state. When I click the icon, I can tell itemList state receives update by having more edit icons that are spaced evenly. However, BasicGrid Row's height is not stretched with Intrinsic.Min.
If I remove IntrinsicSize.Min, even though row's height is stretched, dividers no longer can fillMaxHeight as well as icon columns. without Intrinsic.Min
#Composable
fun ContractSelfPay(chargingViewModel: ChargingViewModel, editClick: () -> Unit = {}) {
val itemList by chargingViewModel.contractSelfPay.observeAsState()
val composeList: List<#Composable () -> Unit> = itemList?.map {
#Composable {
Row {
TempFunc { StyledText(text = it.itemTitle) }
TempFunc { StyledText(text = it.originalPrice.toString()) }
TempFunc { StyledText(text = it.selfPay.toString(), color = self_pay_blue) }
TempFunc { StyledText(text = it.count.toString()) }
TempFunc { StyledText(text = (it.selfPay * it.count).toString()) }
}
}
} ?: listOf()
val total = itemList?.map { (it.selfPay.toInt() * it.count.toInt()) }?.sum() ?: 0
BasicGrid("全自費", composeList, total = total.toString(), editClick = editClick)
}
#Composable
fun BasicGrid(
gridTitle: String,
itemList: List<#Composable () -> Unit>,
total: String = "0",
editClick: () -> Unit = {}
) {
Row(modifier = Modifier.height(IntrinsicSize.Min), verticalAlignment = Alignment.CenterVertically) {
StyledTextBold(text = gridTitle, modifier = Modifier.weight(15f).wrapContentWidth())
VerticalDivider()
Column(
modifier = Modifier.weight(60f)
) {
itemList.forEachIndexed { index, compose ->
compose()
if (index != itemList.size - 1)
HorizontalDivider()
}
if (itemList.isEmpty())
StyledText(text = "尚未有任何紀錄", modifier = Modifier.weight(1f).wrapContentSize())
}
VerticalDivider()
StyledTextBold(text = total, modifier = Modifier.weight(15f).wrapContentWidth())
VerticalDivider()
Column(
modifier = Modifier
.weight(10f)
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceEvenly
) {
itemList.forEachIndexed { index, detail ->
Image(
painter = painterResource(R.drawable.icon_mode_edit),
contentDescription = "",
modifier = Modifier
.align(Alignment.CenterHorizontally)
.clickable { editClick() },
)
if (itemList.isNotEmpty() && index != itemList.size - 1)
HorizontalDivider()
}
}
}
}
I have created issue here https://issuetracker.google.com/issues/217910352. Hopefully it gets solved.
One of the work-arounds I could think of is keeping track of height and removing IntrinsicSize.Min.
As in:
// _key_ is something that causes change of the height of the row
var height by remember(_key_) { mutableStateOf(0) }
Row(Modifier.onSizeChanged { height = it.height }) {
VerticalDivider(Modifier.height(height))
}
In your case I suppose key would be size of itemList.
Thank you Majkeee. It's been a while. The way I fixed it at the time was with custom layout modifier. Not sure if it still works today though.
fun Modifier.expandHeight() = this.then(layout { measurable, constraints ->
val placeable =
measurable.measure(constraints.copy(maxHeight = Constraints.Infinity))
layout(placeable.width, placeable.height) {
placeable.placeRelative(0, 0)
}
})
and to use it you can do
Column(modifier = Modifier.expandHeight())

How to add swipe behavior to the screens?

We have bottom navigation in our app and we want to add swipe behavior into our screens so that if a user swipe to right/left then s/he should be navigated into next screen.
I know that Accompanist has HorizontalPager with Tabs. But I wonder if we can achieve that behavior with bottom navigation.
As you can see in the Material Design Guidelines:
Using swipe gestures on the content area does not navigate between views.
Also:
Avoid using lateral motion to transition between views.
But, if you really want to do this, you can do the this:
fun BottomNavSwipeScreen() {
// This scope is necessary to change the tab using animation
val scope = rememberCoroutineScope()
// I'm using a list of images here
val images = listOf(R.drawable.img1, ...)
// This page state will be used by BottomAppbar and HorizontalPager
val pageState = rememberPagerState(pageCount = images.size)
val scaffoldState = rememberScaffoldState()
Scaffold(
scaffoldState = scaffoldState,
bottomBar = {
BottomAppBar(
backgroundColor = MaterialTheme.colors.primary,
content = {
for (page in images.indices) {
BottomNavigationItem(
icon = {
Icon(Icons.Filled.Home, "Page $page")
},
// here's the trick. the selected tab is based
// on HorizontalPager state.
selected = page == pageState.currentPage,
onClick = {
// When a tab is selected,
// the page is updated
scope.launch {
pageState.animateScrollToPage(page)
}
},
selectedContentColor = Color.Magenta,
unselectedContentColor = Color.LightGray,
label = { Text(text = "Page $page") }
)
}
}
)
},
) {
HorizontalPager(
state = pageState,
offscreenLimit = 2
) { page ->
Image(
painterResource(id = images[page]),
null,
modifier = Modifier
.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
}
}
Here is the result:
you can achieve this by using the animation library from compose:
https://developer.android.com/jetpack/compose/animation
And using the slideIntoContainer animation you can simulate the swipe effect:
composable("route1",
enterTransition = {
slideIntoContainer(
towards = AnimatedContentScope.SlideDirection.Right,
animationSpec = tween(
durationMillis = 250,
easing = LinearEasing // interpolator
)
)
},
exitTransition = {
slideOutOfContainer(
towards = AnimatedContentScope.SlideDirection.Left,
animationSpec = tween(
durationMillis = 250,
easing = LinearEasing
)
)
}) {
Screen1()
}
composable("route2",
enterTransition = {
slideIntoContainer(
towards = AnimatedContentScope.SlideDirection.Left,
animationSpec = tween(
durationMillis = 250,
easing = LinearEasing // interpolator
)
)
},
exitTransition = {
slideOutOfContainer(
towards = AnimatedContentScope.SlideDirection.Right,
animationSpec = tween(
durationMillis = 250,
easing = LinearEasing
)
)
}) {
Screen2()
}

Resources