1

I have top tab view. Each tab returns same composable that shows list of different posts. Initially i fetch tabs and data initial data for each tab. It looks like this:

@Composable
fun Feed() {
    val tabsViewModel: TabsViewModel = hiltViewModel()
    val tabs = tabsViewModel.state.collectAsState()
    val communityFieldViewModel: CommunityFeedViewModel = hiltViewModel()
    val selectedTab = tabsViewModel.selectedTab.collectAsState()
    val indexOfTile = tabs.value.indexOfFirst { it.id == selectedTab.value }
    if(tabs.value.isNotEmpty()) {
        var tabIndex by remember { mutableStateOf(indexOfTile) }
        Column {
            TabRow(selectedTabIndex = tabIndex) {
                tabs.value.forEachIndexed { index, tab ->
                    communityFieldViewModel.loadInitialState(tab.id, tab.tiles, "succeed", tab.token)
                    Tab(selected = tabIndex == index,
                        onClick = { tabIndex = index },
                        text = { Text(text = tab.title) })
                }
            }
            tabs.value.forEachIndexed { _, tab ->
                CommunityFeed(tab.id)
            }
        }
    }
}

My CommunityFeed is getting data from CommunityFeedViewModel and I set initial data for CommunityFeedViewModel in Feed. But problem is that I do not know how to get different data in CommunityFeed and now both render for example all posts and should be different. Here is CommunityFeed and CommunityFeedModel. Any advice?

data class CommunityState(
    val path: String = "",
    val tiles: List<Tile> = emptyList(),
    val loadingState: String = "idle",
    val token: String? = null
)
@HiltViewModel
class CommunityFeedViewModel @Inject constructor(
    private val tileRepository: TileRepository,
): ViewModel() {
    private val state = MutableStateFlow(CommunityState())
    val modelState = state.asStateFlow()
    fun loadInitialState(path: String, tiles: List<Tile>?, loadingState: String, token: String?) {
        val tileList = tiles ?: emptyList()
        state.value = CommunityState(path, tileList, loadingState, token)
    }
}
@Composable
fun CommunityFeed(path: String) {
    val feedViewModel: CommunityFeedViewModel = hiltViewModel()
    val state = feedViewModel.modelState.collectAsState()
    if(state.value.path == path) {
        TileListView(tiles = state.value.tiles, loadMore = {})
    }
}

Jalson1982
  • 287
  • 4
  • 14
  • [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) shouldn't be a repo, it should be only code that can fit here - you can use fake data, e.g. in your example instead of running a query just return static data. – Phil Dukhov Jun 13 '22 at 06:58
  • How do you expect to display different data depending on tag, if you only have a single `modelState`? – Phil Dukhov Jun 13 '22 at 06:58
  • To create different view models for each tab you can use method showed [here](https://stackoverflow.com/a/69371176/3585796) – Phil Dukhov Jun 13 '22 at 06:58
  • `communityFieldViewModel.loadInitialState` running such method from Composable is a bad practice, as per [Thinking in Compose](https://developer.android.com/jetpack/compose/mental-model) all composables should be free of side-effects (which is calling `loadInitialState`). The cleanest way is calling this method in view model `init` block, if you need to re-run it when the view appears, wrap it with `LaunchedEffect`. More info can be found in side effects [documentation](https://developer.android.com/jetpack/compose/side-effects#launchedeffect) – Phil Dukhov Jun 13 '22 at 07:01
  • Ok I will try to add fake data @PhilDukhov. Every tab basically will get same data and those are posts. For example tab a get all posts, tab b get posts from people you follow etc. I tried to use solution to create different view models and it does not return any updated data at all. In the end I need to use single composable that returns list of tiles and view model that will serve different posts to tab. – Jalson1982 Jun 13 '22 at 07:26
  • And how to run it inside init block of viewmodel because i need to wait data to come first. Sorry maybe I am just completely wrong here :( – Jalson1982 Jun 13 '22 at 07:37
  • how is it getting loaded? why do you need to pass any data from composable? Looks like you have `tileRepository`, in your view model you should subscribe to updates from this repository – Phil Dukhov Jun 13 '22 at 08:37
  • I am loading first tabs from server. I get model in response json object like {selectedTab: 'all-posts', tabs:[{id: "all-posts", tiles: [], token: null}, {id: "following", tiles: [{id: "1", text: "Boom"}], token: "123"}] And I need set initial data for each tab and when want to load more i use token. Problem is single composable and single viewmodel to handle all of those cases. – Jalson1982 Jun 13 '22 at 08:41
  • @PhilDukhov here is gist with all components and sample data. https://gist.github.com/Jalson1982/8187f6e10fefded551f3e43392d4481f – Jalson1982 Jun 13 '22 at 10:00
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/245549/discussion-between-jalson1982-and-phil-dukhov). – Jalson1982 Jun 13 '22 at 10:27
  • Nah I need just to find way to pass initial parameters to my hilt viewmodel and all will be good :) – Jalson1982 Jun 13 '22 at 14:31
  • I'm not sure how you can do that with Hilt - it doesn't support a factory like a plain view model does. The only way I know is using `SavedStateHandle`, but it would require each tab to be a separate destination in navigation controller - which is not a bad thing after all – Phil Dukhov Jun 13 '22 at 14:46

0 Answers0