I want to hide a post from my list by clicking a b...
# compose
a
I want to hide a post from my list by clicking a button inside my BottomSheet Layout. My ModalBottomSheetLayout is outside the Scaffold to have it above my BottomAppBar. I’m using the BottomSheet as a options menu for my Post items. Now I need to Hide a post whenever someone clicks on “Hide” inside the BototmSheet. How can I do that now? (code in thread)
Here is my structure:
Copy code
var openModalBottomSheet by remember{ mutableStateOf(false)}
val currentPost: Post? = remember{mutableStateOf(null)}

if(openModalBottomSheet){
    ModalBottomSheetLayout(
        hidePost = { viewModel.hidePost() },
        currentPost = currentPost) {

    }
}

Scaffold(bottomBar = BottomNavigationBar()) {
    MyPostListScreen(
        openModalBottomSheet = { it ->
            openModalBottomSheet = true,
            currentPost.value = it})
}
So whenever the user opens the options for a specific Post it assigns this Post to the currentPost mutableState so I know which one to call the actions on in my BottomSheet. But now I somehow need to access the list inside the MyPostListScreen and filter it for the post that should get hidden. Main problem here is that the List of Posts gets created inside the MyPostListScreen and the Hide action is getting called outside the Scaffold and inside the BottomSheet layout.
f
viewModel.hidePost()
removes the post from the state in your viewmodel which would be observed in your screen where you observe this state.
But now I somehow need to access the list inside the MyPostListScreen and filter it for the post that should get hidden.
Your list inside
MyPostListScreen
should observe the state from which this post will be removed
a
Forgot to mention, MyPostListScreen has its own ViewModel
MyPostListScreen(viewModel: MyPostListScreen = hiltViewModel())
f
You have two options • both viewmodels can observe one repository which will notify the of changes • call
hidePost()
on your
MyPostListScreenViewModel
instead of some other VM you have here
a
How can I call my MyPostListScreenViewModel outside of the MyPostListScreen without having to declare the viewModel above the BottomSheet? The code above is just for simplicity, in total I have 20 different screens with 20 different viewModels all containing a list of Posts.
f
What's wrong with declaring your viewmodel above the bottom sheet?
a
I would have to declare 20 viewModels at the start of my application, shouldn’t this get avoided? So I would no longer tie the viewModel to the scope of the Composable Screen
also I would have to call .hidePost() on all of the 20 viewModels which also looks wrong to me: viewModel1.hidePost() viewModel2.hidePost() viewModel3.hidePost() viewModel4.hidePost() etc because in my BottomSheet I cannot know from which list it should hide the post,
f
You don't have to declare all 20 VMs. And you are right that the VM would have a different lifecycle. You can always go with the shared repository which will notify both viewmodels which is the better option anyways
a
Yes okay thank you, I will look into the first option
f
Ah, I didn't know that all your screens need to observe this
a
I thought about this a lot in the past, putting all my lists in one repository and observing it across the whole app wherever I need them. This would’ve also simplified the process of synchronisation for example when I like a post it gets liked in all lists across the app. So I would simply have a function that goes through all lists and likes a post. Somehow I always refused to do it like this because I thought that would worsen the performance 🤷🏼‍♂️ Having in the absolute worst case 20 lists variables with 100 posts each, how much memory space does this take?
my Post item contains of 15 variables including Author which has another 20. So its a lot of lists that are not necessarily needed at the time but they are still loaded inside the repository. I could also clean up those lists after a user leaves a screen but that would probably make the saveState = true and restoreState = true parameters stop working on my navigation and lists would have to get loaded again every time a user enters a recently opened screen.
But I’ll just give it a try and see if its worsening the performance/memory usage, in the end its just a bunch of strings.
f
Somehow I always refused to do it like this because I thought that would worsen the performance 🤷🏼‍♂️
This is called premature optimisation 😄
Having in the absolute worst case 20 lists variables with 100 posts each
You probably don't have to save the lists separately. If you do, you could end up with one post being in multiple lists which is not desirable. You can have one list with all posts and have each post only once there (assuming that your posts are identifiable with some ID).
a
I’m not quite sure how this would work. For example having two lists: topPostsOfAllTime: List<Post> mostRecetPosts: List<Post> trendingPosts: List<Post> how should I put all this in one List? Btw I’m fetching those lists from my API
f
You save the actual posts in some cache (list with all posts) and whenever you fetch the posts again from API, you update the cache and retrieve the values. If this cache is observable, you can see the changes made to the posts you retrieved by your API in that cache.
This cache can be persistent (database) or in-memory (StateFlow)
a
Ah yeah I have this kind of system implemented with GraphQL NormalizedCache
f
So in a way your
topPostsOfAllTime
could be just a
List<PostId>
and whenever you retrieve them, you check the cache and return the full
Post
entities
a
Okay that sounds like I have a lot to change in my code. Maybe I just put the BottomSheet inside each Screen and hide the BottomNav bar whenever the Sheet pops up. That would simply everything and I would not have to implement all of the above.
f
That's your call 😄 Sometimes it's better to hack something quickly and sometimes you shoud do it properly so it won't bite you in the ass in the future
a
Yeah in my case I’ll just do the quick hack, my complete app is finished and launched 2 days ago, now I only need a hide content option to comply with the UGC Policy 😄
f
I see, good luck 🤞
a
I found the absolute best solution. This way I can leave my BottomSheet outside the Scaffold and also don’t have to rewrite a huge part of my code. 1. I put a SharedFlow of type Post inside my repository:
Copy code
private val _mutedPost: MutableSharedFlow<Post> =
    MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
val mutedPost: MutableSharedFlow<Post> get() = _mutedPost

fun setMutedPost(post: Post) {
    _mutedPost.tryEmit(post)
}
then I call the setMutedPost method from my BottomSheet. 2. I collect any change happening to the mutedPost SharedFlow inside all my viewModels and take the mutedPost Item to filter all lists:
Copy code
init { 
    viewModelScope.launch {
        communityRepository.mutedPost().collect(){ post ->
            hidePostFromAllLists(post.id) //function to filter all lists in this ViewModel
        }
    }
}
Thats it, super simple and just requires a couple lines of code.
d
That can probably be a StateFlow instead, would have slightly cleaner code imo
a
I tried using StateFlow before but somehow only the first ViewModel was collecting the change, I found a solution online where they used a MutableSharedFlow. Not sure why but it was not working