When I select item inside LazyColumn and navigate to this item I can interact with other items from previous screen(item list). Any ideas?
LazyColumn
LazyColumn {
val postList = homeViewModel.state.postList.value
postList?.let {
items(it) { post ->
PostL(
onPostClick = { navigateToPostDetails(post) },
post
)
}
}
}
navigateToPostDetails
fun navigateToPostDetails(post: Post) {
val postJson = Gson().toJson(post)
appNavController.navigate("postDetails/$postJson")
}
Related
When I modify the properties of the objects in the List, the UI does not update
my code:
#OptIn(ExperimentalFoundationApi::class)
#Composable
fun ContactCard(
) {
var stateList = remember {
mutableStateListOf<ListViewData>()
}
viewModel!!.recordRespListLiveData!!.observe(this) { it ->
it.forEach {
stateList.add(ListViewData(false, it))
}
}
LazyColumn() {
stateList.forEachIndexed { index, bean ->
stickyHeader() {
Box(Modifier.clickable {
stateList[index].visible = true
}) {
ContactNameCard(bean.data.contact, index)
}
}
items(bean.data.records) { data ->
if (bean.visible) {
RecordItemCard(record = data)
}
}
}
}
}
When I click on the Box, visible is set to true, but the RecordItemCard doesn't show,why?
For SnapshotList to trigger you need to add, delete or update existing item with new instance. Currently you are updating visible property of existing item.
If ListViewData is instance from data class you can do it as
stateList[index] = stateList[index].copy(visible = true)
How to make UI changes in a paginated list with jetpack compose.
Use case
I have a paginated list which has data name(string) and like(boolean). If i click on the particular item in the list, i need to place a like button in the UI. But the image is not updated in UI based on condition.
Snippet
userList -> LazyPagingItems<AllDoctorsResponse.Data.Doctor>
//viewModel
userList.itemSnapshotList.find { it?.id == user.id }?.liked = true
// Composable
items(userList.itemCount){ index ->
userList[index]?.let {
if (it.liked == true) {
UserCardWithLike(it, onClick = { userId ->
onUserCardClicked(userId)
}, onLikeChange = { isLiked, user ->
onLikeChange(isLiked, user)
})
} else {
UserCard(it, onClick = { userId ->
onUserCardClicked(userId)
}, onLikeChange = { isLiked, user ->
onLikeChange(isLiked, user)
})
}
}
}
I don't use paging3 anymore, so I didn't test this code, but I think it will work:
items(userList.itemCount){ index ->
userList[index]?.let {
var liked by rememberSaveable { mutableStateOf(it.liked) }
if (liked == true) {
UserCardWithLike(
it,
onClick = { userId -> onUserCardClicked(userId) },
onLikeChange = { isLiked, user -> liked = false }
)
} else {
UserCard(
it,
onClick = { userId -> onUserCardClicked(userId) },
onLikeChange = { isLiked, user -> liked = true }
)
}
}
}
If you want something like notifyItemChange, it's not possible in Paging3. In that case, I suggest trying to rewrite the paging library, which is surprisingly easy.
https://gist.github.com/FishHawk/6e4706646401bea20242bdfad5d86a9e
I have LazyColumn with swipe-to-delete. When I swipe an item, it is deleted by viewModel. The problem is that if I swipe the item away, the LazyColumn doesn't update the position of other items (as shown in GIF).
Here's my code implementation:
#ExperimentalMaterialApi
#Composable
fun Screen() {
val livedata = viewModel.itemsLiveData.observeAsState()
val stateList = remember { mutableStateListOf<Data>() }
stateList.addAll(livedata.value!!)
SwipableLazyColumn(stateList)
}
#ExperimentalMaterialApi
#Composable
fun SwipableLazyColumn(
stateList: SnapshotStateList<Data>
) {
LazyColumn {
items(items = stateList) { item ->
val dismissState = rememberDismissState()
if (dismissState.isDismissed(EndToStart) || dismissState.isDismissed(StartToEnd)) {
viewModel.swipeToDelete(item)
}
SwipeToDismiss(
state = dismissState,
directions = setOf(StartToEnd, EndToStart),
dismissThresholds = {
FractionalThreshold(0.25f)
},
background = {},
dismissContent = {
MyData(item)
}
)
}
}
}
I use SnapshotStateList as it's suggested here. Although I don't use swapList because it clears out all items
ViewModel:
class MyViewModel #Inject internal constructor(
private val itemRepository: ItemRepository
) : BaseViewModel(), LifecycleObserver {
private val itemsList = mutableListOf<MyData>()
private val _itemsLiveData = MutableLiveData<List<MyData>>()
val itemsLiveData: LiveData<List<MyData>> = _itemsLiveData
init {
loadItems()
}
private fun loadItems() {
viewModelScope.launch {
itemRepository.getItems().collect {
when (it) {
is Result.Success -> onItemsLoaded(it.data)
is Result.Error -> {
onItemsLoaded(emptyList())
}
}
}
}
}
private fun onItemsLoaded(itemsList: List<MyData>) {
itemsList.clear()
itemsList.addAll(notifications)
_itemsLiveData.value = if (itemsList.isNotEmpty()) {
itemsList
} else {
null
}
}
fun swipeToDelete(item: MyData) {
if (itemsList.size == 0) return
viewModelScope.launch {
when (
val result =
itemRepository.deletelItem(item)
) {
is Result.Success -> {
onItemDeleted(item)
}
is Result.Error -> {
showSnackBar(
"error"
)
}
}
}
}
private fun onItemDeleted(item: MyData) {
itemsList.remove(item)
_itemsLiveData.value = itemsList
}
}
You should refresh your list inside viemodel(delete item) and return modified list right here
var tempList = itemList
ItemList.clear()
ItemList.addAll(tempList)
You need to provide key for the LazyColumn's items.
By default, each item's state is keyed against the position of the
item in the list. However, this can cause issues if the data set
changes, since items which change position effectively lose any
remembered state.
Example
LazyColumn {
items(
items = stateList,
key = { _, listItem ->
listItem.hashCode()
},
) { item ->
// As it is ...
}
}
Reference
I have implemented LazyColumn with Paging, but I'm now trying to add sticky headers as well.
The stickyHeader() function is not available inside the items() scope, so I don't see how this should work.
#Composable
fun MovieList(movies: Flow<PagingData<Movie>>) {
val lazyMovieItems: LazyPagingItems<Movie> = movies.collectAsLazyPagingItems()
LazyColumn {
// TODO: Add sticky headers
items(lazyMovieItems) { movie ->
MovieItem(movie = movie!!)
}
}
}
How can I add the stickyHeaders?
#Composable
fun MovieList(movies: Flow<PagingData<Movie>>) {
val lazyMovieItems = movies.collectAsLazyPagingItems()
LazyColumn {
val itemCount = lazyMovieItems.itemCount
var lastCharacter: Char? = null
for (index in 0 until itemCount) {
// Gets item without notifying Paging of the item access,
// which would otherwise trigger page loads
val movie = lazyMovieItems.peek(index)
val character = movie?.name?.first()
if (movie !== null && character != lastCharacter) {
stickyHeader(key = character) {
MovieHeader(character)
}
}
item(key = movie?.id) {
// Gets item, triggering page loads if needed
val movieItem = lazyMovieItems[index]
Movie(movieItem)
}
lastCharacter = character
}
}
}
I have a screen code like this (Shows a simple list).
What I'm seeking, is to delete the item when it was clicked.
How can I achieve this?
HorizontalScroller {
Row(modifier = Spacing(bottom = 16.dp, right = 16.dp)) {
posts.forEach { post ->
WidthSpacer(16.dp)
Clickable(onClick = {
// Delete the PostCardPopular I just added if it was clicked
}) {
PostCardPopular(post)
}
}
}
}
you can achieve this using a model Object and ModelList
#Model
object YourModel {
val posts = ModelList<Post>()
}
.
.
.
HorizontalScroller {
Row(modifier = Spacing(bottom = 16.dp, right = 16.dp)) {
for(post in YourModel.posts)
WidthSpacer(16.dp)
Clickable(onClick = {
YourModel.posts.remove(post)
}) {
PostCardPopular(post)
}
}
}
}
When you remove from ModelList, the UI will recompose.
Extra: Google releases a codelab for basic compose. https://codelabs.developers.google.com/codelabs/jetpack-compose-basics/