Tlaster / PreCompose

Compose Multiplatform Navigation && State Management

Home Page:https://tlaster.github.io/PreCompose/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Navigating is losing Screen state

JagadishaIncture opened this issue · 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

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

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)
                    }
                }
            }
        }
}

}`

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 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
                }
            }
        }
    }
}

}`

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() }) } ) }

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.

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

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

you suggets me here

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

val _uiState = MutableSharedFlow()

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)
}

}`