PreCompose icon indicating copy to clipboard operation
PreCompose copied to clipboard

Navigating is losing Screen state

Open JagadishaIncture opened this issue 1 year ago • 8 comments

Describe the bug When we navigate from Screen A to Screen B and coming back to Screen A which leads to recreate screen A . means screen start recreates and re hits api call and fetches data

Expected behavior Screen A state should be retained

JagadishaIncture avatar May 09 '24 08:05 JagadishaIncture

Hi can you provide some sample code that reproduce this issue?

Tlaster avatar May 09 '24 09:05 Tlaster

Screen A

` @Composable fun GroupsScreen( viewModel: GroupViewModel, baseViewModel: BaseViewModel, platformUtils: PlatformUtils, navigator: Navigator, localSharedStorage: LocalSharedStorage, showToolBar:Boolean=false ) {

var isLoading by remember { mutableStateOf(false) }
var isShimming by remember { mutableStateOf(false) }
var data: List<GroupsItemModel> by remember { mutableStateOf(ArrayList()) }

var currentPage by remember { mutableStateOf(0) }
val pageCount = 10
val totalRecords = 0

val lazyListState = rememberLazyGridState()

if (isLoading) {
    DialogCustomCircleProgressbar()
}

Scaffold {

        Column(
            modifier = Modifier.fillMaxWidth().fillMaxHeight().padding(top = 5.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            if (showToolBar) {
                ToolBarWithBack(navigator = navigator, stringResource(MR.strings.groups))
            }
            if (isShimming) {
                GroupScreenShimmer(platformUtils)
            }else{
            LazyVerticalGrid(
                columns = GridCells.Fixed(if (platformUtils.isTablet()) 2 else 1),
                state = lazyListState,
                modifier = Modifier.fillMaxHeight(1f).fillMaxWidth()
            ) {
                itemsIndexed(data) { _, item ->
                    Card(
                        shape = RoundedCornerShape(8.dp),
                        modifier = Modifier
                            .padding(8.dp)
                            .fillMaxWidth()
                            .clickable {
                                val id = item.groupId.toString()
                                if(showToolBar){
                                    navigator.navigate(NavigationRoute.GroupsDetailScreen.getRoute(id))
                                }
                                else{
                                    navigator.navigate(NavigationRoute.GroupsDetailScreenAdmin.getRoute(id))
                                }

                            },
                        elevation = 2.dp
                    ) {
                        Row(modifier = Modifier.fillMaxWidth()) {

                            RoundedCornerImageView(platformUtils,item.profileImage,Modifier.fillMaxWidth(0.3f).height(100.dp).padding(5.dp))

                            Column(modifier = Modifier.weight(1f).padding(8.dp), verticalArrangement = Arrangement.Center) {
                                Text(
                                    text = item.name.toString(),
                                    style = StyleUtils.getBoldFontStyle()
                                )
                                Spacer(modifier = Modifier.height(3.dp))
                                Text(
                                    text = item.description.toString(),
                                    maxLines = 1,
                                    style = StyleUtils.getSemiBoldFontStyle(),
                                    overflow = TextOverflow.Ellipsis
                                )
                            }
                            if (showToolBar) {
                                Box(modifier = Modifier.align(Alignment.CenterVertically)) {
                                    Spacer(modifier = Modifier.width(8.dp))
                                    DeleteButton(modifier = Modifier.size(40.dp)) {
                                        baseViewModel.deletePost(
                                            category = Category.GROUP.name,
                                            item.groupId.toString(),
                                            localSharedStorage.getUserId()
                                        )
                                    }
                                }
                            }
                        }
                    }
                }
            }


        }
    }
}




LaunchedEffect(Unit)
{
    viewModel.getGroupPlace(currentPage, pageCount)

    viewModel._uiState.collect {

        when {
            it.isLoading -> {
                if (currentPage==0)
                {
                    isShimming = true
                }else{
                    isLoading =true
                }

            }


            it.error.isNotEmpty() -> {
                isShimming = false
                isLoading =false
                platformUtils.makeToast(it.error)
            }

            it.data != null -> {
                isShimming = false
                isLoading =false
                data = it.data
            }

        }
    }
}

LaunchedEffect(Unit)
{

    baseViewModel._deleteResponse.collect {

        when {
            it.isLoading -> {
                isLoading = true
            }

            it.error.isNotEmpty() -> {
                isLoading = false
                platformUtils.makeToast(it.error)
            }

            it.success != null -> {
                isLoading = false
                platformUtils.makeToast(it.success)
                currentPage=0
                viewModel.getGroupPlace(currentPage, pageCount)
            }

        }
    }
}

LaunchedEffect(lazyListState) {
    snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index }
        .collect { lastIndex ->
            if (lastIndex != null && lastIndex >= data.size - 1 && !isLoading) {
                if(data.size < totalRecords){
                    if(platformUtils.isOnline()){
                        currentPage++
                        viewModel.getGroupPlace(currentPage, pageCount)
                    }
                }
            }
        }
}

}`

JagadishaIncture avatar May 09 '24 09:05 JagadishaIncture

Screen B

`@OptIn(ExperimentalAdaptiveApi::class) @Composable fun GroupsDetailScreen( navigator: Navigator, viewModel: GroupViewModel, platformUtils: PlatformUtils, id: String, showToolBar: Boolean = false ) { var isShimming by remember { mutableStateOf(false) } var data: List<GroupsDetailItemModel> by remember { mutableStateOf(emptyList()) } val lazyListState = rememberLazyGridState()

Scaffold(
    topBar = {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .background(color = ColorResources.ComponentColor),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Column(modifier = Modifier.weight(.8f)) {
                ToolBarWithBack(
                    navigator = navigator,
                    title = stringResource(MR.strings.concert)
                )
            }
            Column {
                AdaptiveIconButton(onClick = {
                    if (showToolBar) {
                        navigator.navigate(NavigationRoute.UserInGroupScreen.getRoute(id))
                    } else {
                        navigator.navigate(NavigationRoute.UserInGroupScreenAdmin.getRoute(id))
                    }
                }) {
                    Icon(
                        imageVector = Icons.Outlined.Person,
                        contentDescription = null,
                        tint = ColorResources.IconColor
                    )
                }
            }
        }
    }
) {
    if (isShimming) {
        GroupDetailScreenShimmer(platformUtils)
    } else {
        Column {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 16.dp, vertical = 8.dp)
            ) {
                if (showToolBar) {
                    Box(
                        modifier = Modifier
                            .background(
                                color = Color.Green,
                                shape = RoundedCornerShape(12.dp)
                            )
                            .padding(horizontal = 8.dp, vertical = 4.dp)
                            .clickable {
                                navigator.navigate(NavigationRoute.SendMessageScreen.getRoute(id))
                            }
                            .align(Alignment.TopEnd)
                    ) {
                        Text(
                            text = stringResource(MR.strings.send),
                            color = Color.White,
                            fontWeight = FontWeight.Bold,
                            modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
                        )
                    }
                }
            }

            LazyVerticalGrid(
                columns = GridCells.Fixed(if (platformUtils.isTablet()) 2 else 1),
                state = lazyListState,
                modifier = Modifier.fillMaxSize().padding(top = 5.dp)
            ) {
                if (data.isEmpty()) {
                    item {
                        NoDataView()
                    }
                } else {
                    itemsIndexed(data) { index, item ->
                        Card(
                            shape = RoundedCornerShape(12.dp),
                            elevation = 8.dp,
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(8.dp)
                        ) {
                            Column(
                                modifier = Modifier.padding(16.dp)
                            ) {
                                Row(verticalAlignment = Alignment.CenterVertically) {
                                    Box(
                                        modifier = Modifier
                                            .size(40.dp)
                                            .background(color = Color.DarkGray, shape = CircleShape),
                                        contentAlignment = Alignment.Center
                                    ) {
                                        Text(
                                            text = item.createdByName?.take(1)?.uppercase() ?: "",
                                            color = Color.White,
                                            fontSize = 20.sp,
                                            fontWeight = FontWeight.Bold
                                        )
                                    }
                                    Spacer(modifier = Modifier.width(8.dp))
                                    Text(
                                        text = item.createdByName ?: "",
                                        style = TextStyle(
                                            fontWeight = FontWeight.Bold,
                                            fontSize = 16.sp
                                        ),
                                        modifier = Modifier.weight(1f)
                                    )
                                }
                                Spacer(modifier = Modifier.height(16.dp))
                                Spacer(modifier = Modifier.weight(1f))
                                Row(
                                    modifier = Modifier.fillMaxWidth(),
                                    horizontalArrangement = Arrangement.SpaceBetween
                                ) {
                                    Text(
                                        text = item.textContent ?: "",
                                        style = TextStyle(
                                            fontWeight = FontWeight.Light,
                                            color = Color.Black
                                        )
                                    )
                                    Text(
                                        text = TimeConversionClass.convertLongToDate(item.createdOn!!.toLong()),
                                        style = TextStyle(
                                            fontWeight = FontWeight.Light,
                                            color = Color.Gray
                                        )
                                    )
                                }
                                Spacer(modifier = Modifier.height(16.dp))
                                RoundedCornerImageView(
                                    platformUtils,
                                    item.imageContent,
                                    Modifier.height(200.dp).padding(6.dp)
                                )
                            }
                        }
                    }
                }
            }
        }
    }

    LaunchedEffect(Unit) {
        viewModel.getGroupsItemDetail(id, getnoOfRecordsPerPage = 4, pageNumber = 0)

        viewModel._uidetailState.collect { uiState ->
            when {
                uiState.isLoading -> {
                    isShimming = true
                }
                uiState.error.isNotEmpty() -> {
                    isShimming = false
                    platformUtils.makeToast(uiState.error)
                }
                uiState.data != null -> {
                    isShimming = false
                    data = uiState.data
                }
            }
        }
    }
}

}`

JagadishaIncture avatar May 09 '24 09:05 JagadishaIncture

ToolBar code

@OptIn(ExperimentalAdaptiveApi::class) @Composable fun ToolBarWithBack(navigator: Navigator,title: String) { AdaptiveTopAppBar( title = { Text(title,style = StyleUtils.getBoldFontStyle()) }, navigationIcon = { Image(imageVector = Icons.Default.KeyboardArrowLeft, contentDescription = null, colorFilter = ColorFilter.tint(color = ColorResources.IconColor),modifier = Modifier.clickable { navigator.goBack() }) } ) }

JagadishaIncture avatar May 09 '24 09:05 JagadishaIncture

Seems like you're fetching from LaunchedEffect

LaunchedEffect(Unit)
{
    viewModel.getGroupPlace(currentPage, pageCount)
    //...
}

LaunchedEffect(Unit) will run When LaunchedEffect enters the Composition, in this case, when you're navigating from GroupsScreen to other screen, you GroupsScreen is actually cleared from the composition, so when you're back from other screen to GroupsScreen, the GroupsScreen is entering the composition once again, which triggers LaunchedEffect to run once again.

Tlaster avatar May 09 '24 09:05 Tlaster

any solution her how to avoid api call when returning back to GroupsScreen?

JagadishaIncture avatar May 09 '24 09:05 JagadishaIncture

You can place your API call in ViewModel's initializer.

Tlaster avatar May 09 '24 09:05 Tlaster

you suggets me here

`class GroupViewModel(private val mainUseCase: MainUseCase) : ViewModel() {

val _uiState = MutableSharedFlow<GroupStateHolder>()

fun getGroupPlace(currentPage: Int, pageCount: Int) {

    val groupPayloadModel = CategoryPayloadModel().apply {
        statusList = arrayListOf(Status.ACTIVE.name)
        pageNumber = currentPage
        noOfRecordsPerPage = pageCount
    }

    mainUseCase.getGroups(groupPayloadModel).onEach { res ->
        when (res) {
            is NetworkResult.Loading -> {
                _uiState.emit(GroupStateHolder(isLoading = true))
            }

            is NetworkResult.Success -> {
                data = emptyList()
                data = data + (res.data ?: emptyList())
                _uiState.emit(GroupStateHolder(data = data, totalRecords = res.totalRecords))
            }

            is NetworkResult.Error -> {
                _uiState.emit(GroupStateHolder(error = res.message))
            }
        }
    }.launchIn(viewModelScope)
}

}`

JagadishaIncture avatar May 09 '24 09:05 JagadishaIncture