Search code examples
kotlinkotlin-stateflow

State flow Android Kotlin


I have a god view model for every thing I know this is wrong but I am just experimenting with Flow

I have these two State flow variables in view model

 private val _currentRestroMenu = MutableStateFlow<State<Menu>>(State.loading())
 private val _userCart = MutableStateFlow(CustomerCart())

 val currentRestroMenu: StateFlow<State<Menu>> = _currentRestroMenu
 val userCart: StateFlow<CustomerCart> = _userCart

Below functions get data from server and update above state flow

private fun getRestroMenuFromCloudAndUpdateData(restroId: String) = viewModelScope.launch {
        fireStoreRepository.getRestroMenu(restroId).collect { state ->
            when (state) {
                is State.Success -> {
                    _currentRestroMenu.value = State.success(state.data)
                    dataHolderMenuOnSearch = state.data
                    if (!viewedRestroMenu.contains(state.data)) {
                        viewedRestroMenu.add(state.data)
                    }

                }
                is State.Failed -> {
                    _currentRestroMenu.value = State.failed(state.message)
                }
                is State.Loading -> {
                    _currentRestroMenu.value = State.loading()
                }
            }


        }
    }



private fun getCart() = viewModelScope.launch(Dispatchers.IO) {
        if (currentCart.cartEmpty) {
            fireStoreRepository.getUserCartInfoFromCloud(dataStoreRepository.readFileDataStoreValue.first().savedUserId)
                .collect { cartState ->
                    when (cartState) {
                        is State.Success -> {
                            _userCart.update {
                                it.copy(
                                    cartId = cartState.data.cartId,
                                    cartEmpty = cartState.data.cartEmpty,
                                    cartItem = cartState.data.getCartItem(),
                                    restroId = cartState.data.restroId,
                                    cartTotalAmount = cartState.data.cartTotalAmount,
                                    cartAddressId = cartState.data.cartAddressId,
                                    cartDeliveryTime = cartState.data.cartDeliveryTime,
                                    cartCookingInstructions = cartState.data.cartCookingInstructions,
                                    cartAppliedOfferId = cartState.data.cartAppliedOfferId,
                                    deliveryPartnerTipAmount = cartState.data.deliveryPartnerTipAmount,
                                    cartDeliveryCharge = cartState.data.cartDeliveryCharge,
                                    cartTax = cartState.data.cartTax,
                                    deliveryInstructionId = cartState.data.deliveryInstructionId,
                                    foodHandlingCharge = cartState.data.foodHandlingCharge,
                                    cartNumberOfItems = cartState.data.cartNumberOfItems,
                                    cartRestroName = cartState.data.cartRestroName
                                )
                            }
                            currentCart = cartState.data

                        }
                        is State.Failed -> {
                            if (cartState.message == "Result null") {
                                Log.d(
                                    ContentValues.TAG,
                                    "getCartFromCloud: No cart details found in cloud creating new cart"
                                )
                                _userCart.update {
                                    it.copy(
                                        cartId = dataStoreRepository.readFileDataStoreValue.first().savedUserId,
                                        cartEmpty = true
                                    )
                                }
                                currentCart = CustomerCart(
                                    cartId = dataStoreRepository.readFileDataStoreValue.first().savedUserId,
                                    cartEmpty = true
                                )
                            }
                        }
                        is State.Loading -> {
                            Log.d(ContentValues.TAG, "getCartFromCloud: Loading")
                        }
                    }

                }
        } else {
            _userCart.value = currentCart
            Log.d(ContentValues.TAG, "getCart: $currentCart ")
        }


    }

I am collecting these state flow from different fragments every thing works fine except one fragment

here is the code

in on create method

viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {

                    godCustomerViewModel.currentRestroMenu.collectLatest { menuState ->
                        Log.d(TAG, "currentRestroMenu ::: mENUSELECT FIRED: ")
                        when (menuState) {
                            is State.Success -> {
                                restroMenu = menuState.data
                                binding.recyclerView2.hideShimmer()
                                getCartDetails(restroMenu)
                            }
                            is State.Failed -> {
                                Log.d(TAG, "currentRestroMenu:  ")
                            }
                            is State.Loading -> {
                                binding.recyclerView2.showShimmer()
                            }
                        }
                    }



            }
        }

private fun getCartDetails(restroMenu: Menu) = viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {

            godCustomerViewModel.userCart.collectLatest {

                if (it.restroId == restroMenu.restroId) {

                    categoryAdapterRestroDetails.setData(
                        restroMenu.menuCategories,
                        it.getCartItem()
                    )
                } else {
                    categoryAdapterRestroDetails.setData(
                        restroMenu.menuCategories,
                        ArrayList()
                    )
                }
            }
        }
    }

I am passing the two collected values to adapter (retro menu and item in cart )

when the fragment is loaded for the first time everything works fine

I have add dish to cart function which updates the value of user cart

 fun addDishToCart(dish: Dish) = viewModelScope.launch {
        Log.d(ContentValues.TAG, "addDishToCart:  view model invoked")
        if (currentCart.checkIfCartBelongsToThisRestro(dish.dishRestroId)) {
            currentCart.addDishToCart(dish).collect {
                Log.d(ContentValues.TAG, "addDishToCartcollect: $currentCart")
                _userCart.update {
                    it.copy(
                        cartEmpty = currentCart.cartEmpty,
                        cartItem = currentCart.getCartItem(),
                        restroId = currentCart.restroId,
                        cartTotalAmount = currentCart.cartTotalAmount,
                        cartNumberOfItems = currentCart.cartNumberOfItems,

                        )
                }
            }


        } else {
            // restro Conflict
            Log.d(ContentValues.TAG, "addDishToCart: $currentCart")
            _restroConflict.value = CartConflict(true, currentCart.cartRestroName, dish)
        }
        Log.d(ContentValues.TAG, "addDishToCart current cart: ${currentCart.getCartItem()}")
        Log.d(ContentValues.TAG, "addDishToCart: user Cart : ${_userCart.value.getCartItem()} ")

    }

Which also work fine initially

I also have a button to filter menu to veg non veg

fun filterMenuForVeg(value: Boolean, showAll: Boolean) = viewModelScope.launch {

        if (!showAll) {
            Log.d(ContentValues.TAG, "filterMenuForVeg: Entered veg :$value")

            var filteredMenu = Menu()
            filteredMenu.restroId = dataHolderMenuOnSearch.restroId


            for (menuCategory in dataHolderMenuOnSearch.menuCategories) {
                Log.d(ContentValues.TAG, "filterMenuForVeg: $dataHolderMenuOnSearch ")
                for (dish in menuCategory.dishes) {
                    if (dish.dishVeg == value) {
                        Log.d(ContentValues.TAG, "found dish with veg $value: ")
                        var categoryAlreadySaved = false
                        filteredMenu.menuCategories.filter {
                            categoryAlreadySaved = it.categoryId == menuCategory.categoryId
                            true
                        }
                        if (!categoryAlreadySaved) {
                            Log.d(ContentValues.TAG, "menu category not found in filtered list ")
                            val menuCategoryToAdd = MenuCategories()
                            menuCategoryToAdd.menuCategoryName = menuCategory.menuCategoryName
                            menuCategoryToAdd.categoryId = menuCategory.categoryId
                            menuCategoryToAdd.restroId = menuCategory.restroId
                            menuCategoryToAdd.dishes.add(dish)
                            filteredMenu.menuCategories.add(menuCategoryToAdd)
                        } else {
                            Log.d(ContentValues.TAG, "menu category  found in filtered list ")
                            filteredMenu.menuCategories.find {
                                if (it.categoryId == menuCategory.categoryId) {
                                    it.restroId = menuCategory.restroId
                                    it.dishes.add(dish)
                                }
                                true
                            }
                        }
                    }
                }
            }
            Log.d(ContentValues.TAG, "filterMenuForVeg : $filteredMenu ")
            _currentRestroMenu.value = State.success(filteredMenu)
        } else {
            // set to all data
            _currentRestroMenu.value = State.success(dataHolderMenuOnSearch)
        }

When I filter dish for veg or non veg then add dish to cart (Which only changes userCart State flow) the place where I am collecting these state flow get fired twice so set data to adapter is getting called twice

What Iam doing wrong


Solution

  • Could you collect the items with onEach instead of collectLatest? It would solve your problem probably.