'Combining Two Flows and add them to temporary list of StateFlow(Hot Flows)

I m getting data from two End points using flows and assigning those two list to temporary list in ViewModel. For this purpose, I'm using combine function and returning result as stateFlows with stateIn operator but that's not working. Can anyone point me out where I go wrong please.

ViewModel.kt

private val _movieItem: MutableStateFlow<State<List<HomeRecyclerViewItems>>> =
        MutableStateFlow(State.Loading())
val movieItems: StateFlow<State<List<HomeRecyclerViewItems>>> = _movieItem

fun getHomeItemList() {
        viewModelScope.launch {
        val testList: Flow<State<List<HomeRecyclerViewItems.Movie>>> =
                settingsRepo.getMovieList().map {
                    State.fromResource(it)
                }
        val directorList: Flow<State<List<HomeRecyclerViewItems.Directors>>> =
                settingsRepo.getDirectorList().map {
                    State.fromResource(it)
                }
        _movieItem.value = combine(testList, directorList) { testList, directorList ->
                  testList + directorList // This is not working as "+" Unresolve Error
             }.stateIn(
                 viewModelScope,
                 SharingStarted.WhileSubscribed(5000),
                 State.loading<Nothing>()
             ) as State<List<HomeRecyclerViewItems>> // Unchecked cast: StateFlow<Any> to State<List<HomeRecyclerViewItems>>
}

Repository.kt

fun getMovieList(): Flow<ResponseAPI<List<HomeRecyclerViewItems.Movie>>> {
        return object :
            NetworkBoundRepository<List<HomeRecyclerViewItems.Movie>, List<HomeRecyclerViewItems.Movie>>() {
            override suspend fun saveRemoteData(response: List<HomeRecyclerViewItems.Movie>) {

            }

            override fun fetchFromLocal() {
            }

            override suspend fun fetchFromRemote(): Response<List<HomeRecyclerViewItems.Movie>> =
                apiInterface.getMoviesList()
        }.asFlow()
    }

    fun getDirectorList(): Flow<ResponseAPI<List<HomeRecyclerViewItems.Directors>>> {
        return object :
            NetworkBoundRepository<List<HomeRecyclerViewItems.Directors>, List<HomeRecyclerViewItems.Directors>>() {
            override suspend fun saveRemoteData(response: List<HomeRecyclerViewItems.Directors>) {

            }

            override fun fetchFromLocal() {
            }

            override suspend fun fetchFromRemote(): Response<List<HomeRecyclerViewItems.Directors>> =
                apiInterface.getDirectorsList()

        }.asFlow()
    }

Network BoundRepository.kt

@ExperimentalCoroutinesApi
abstract class NetworkBoundRepository<RESULT, REQUEST> {

    fun asFlow() = flow<ResponseAPI<REQUEST>> {

        val apiResponse = fetchFromRemote()

        val remotePosts = apiResponse.body()

        
        if (apiResponse.isSuccessful && remotePosts != null) {
          
            emit(ResponseAPI.Success(remotePosts))
        } else {
            emit(ResponseAPI.Failed(apiResponse.errorBody()!!.string()))
        }

    }.catch { e ->
        e.printStackTrace()
        emit(ResponseAPI.Failed("Server Problem! Please try again Later. "))
    }

    @WorkerThread
    protected abstract suspend fun saveRemoteData(response: REQUEST)

    @MainThread
    protected abstract fun fetchFromLocal()

    @MainThread
    protected abstract suspend fun fetchFromRemote(): Response<REQUEST>
}

Endpoints with Sealed Class

@GET("directors")
        fun getDirectorsList(): Response<List<HomeRecyclerViewItems.Directors>>
@GET("movies")
    fun getMoviesList(): Response<List<HomeRecyclerViewItems.Movie>>
sealed class HomeRecyclerViewItems {
    class Title(
        val id: Int,
        val title: String
        ) : HomeRecyclerViewItems()

    class Movie(
        val id: Int,
        val title: String,
        val thumbnail: String,
        val releaseDate: String
    ) : HomeRecyclerViewItems()

    class Directors(
        val id: Int,
        val name: String,
        val avator: String,
        val movie_count: Int
    ) : HomeRecyclerViewItems()
}

Fragment.kt

@AndroidEntryPoint
@ExperimentalCoroutinesApi
class SettingsFragment : BaseBottomTabFragment() {
    private var _binding: FragmentSettingsBinding? = null
    private val binding get() = _binding!!

    private val viewModel by viewModels<SettingViewModel>()
    @Inject
    lateinit var recyclerViewAdapter: RecyclerViewAdapter
   @Inject
   lateinit var bundle: Bundle
    var finalList = mutableListOf<HomeRecyclerViewItems>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        _binding = FragmentSettingsBinding.inflate(layoutInflater,container,false)
        val view = binding.root
        binding.rvMovie.apply {
            setHasFixedSize(true)
            layoutManager = LinearLayoutManager(activity)
        }
                bundle.putString("Hello","hihg")
        Toast.makeText(activity, "${bundle.getString("Hello")}", Toast.LENGTH_SHORT).show()
        finalList.add(HomeRecyclerViewItems.Title(1,"hello"))



        return view
    }

    private fun observeList() {
        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED){
                launch {
                    viewModel.movieItems.collect { state ->
                        when(state){
                            is State.Loading ->{
                               
                            }
                            is State.Success->{
                                if (state.data.isNotEmpty()){
                                    recyclerViewAdapter = RecyclerViewAdapter()
                                    binding.rvMovie.adapter = recyclerViewAdapter
                                    recyclerViewAdapter.submitList(finalList)
                                }
                            }
                            is State.Error -> {
                                Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
                            }
                            else -> Unit
                        }

                    }
                }
               
            }
        }
    }



    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        (activity as MainActivity).binding.ivSearch.isGone = true
        viewModel.getHomeItemList()
        observeList()
    }
    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

Note: I m following this tutorial simpliedCoding for api data for multirecyclerview but want to implement it with Kotlin State Flow. Any help in this regard is highly appreciated. Thanks.



Solution 1:[1]

Your problem is in here

val testList: Flow<State<List<HomeRecyclerViewItems.Movie>>> =
                settingsRepo.getMovieList().map {
                    State.fromResource(it)
                }
val directorList: Flow<State<List<HomeRecyclerViewItems.Directors>>> =
                settingsRepo.getDirectorList().map {
                    State.fromResource(it)
                }
_movieItem.value = combine(testList, directorList) { testList, directorList ->
                  testList + directorList
             }

They are not returning a List<HomeRecyclerViewItems>, but a State<List<HomeRecyclerViewItems>. Maybe a better name for the variables are testsState and directorsState. After that it will be more clear why you need to unpack the values before combining the lists

_movieItem.value = combine(testsState, directorsState) { testsState, directorsState ->
                  val homeRecyclerViewItems = mutableListOf<HomeRecyclerViewItems>()
                  if (testsState is Success) homeRecyclerViewItems.add(testsState.data)
                  if (directorsState is Success) homeRecyclerViewItems.add(directorsState.data)
                  homeRecyclerViewItems
             }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Hasansidd