Hi everyone I am stuck on my project and hoping to...
# android
h
Hi everyone I am stuck on my project and hoping to get some help on it. So I'm basically working on an app which is somewhat similar to Linkedin but only for my University. So there are two types of profiles one is User and one is Teams So I'm getting error on the Team Screen. So there's a screen where an api call is made and all available teams are listed. Once you click on one of the team card then it opens a new screen with that particular team details. To fetch these team details I have to pass a slug to api endpoint. I'm working in Jetpack Compose and I'm passing the slug through nav args and I make the api call inside the
Launched Effect.
So everything is working fine but just the issue is if I try to rapidly switch between team pages then the value of slug is getting glitched. Like it is being passed properly through nav args but Ig the re-composition of screen/composable is causing abnormal changes in slug value. I'm not sure what's the issue and I need help figuring it out.
😶 4
j
I'm pretty sure that without any code we cannot help you. It's just guess game at this point
Also, a little off-top - look at appyx or other compose navigation alternative. Leave that url nonsense behind, you won't ever look back 🙂
h
Yeah I have those that nav library migration in the plan. but it would take some time to migrate on it
this is the screenshot of logcat
I'll attach code snippets for ref shortly
j
well the logcat suggests that 2 other parameters were passed, is there a slug named 3XglI-ieee?
maybe you are just looking at some race condition?
h
so the actual team card I'm clicking on its value is
3XglI-ieee
and that is what technically should be there, but for some reason a previously clicked team page's slug is entering.
YUZxM-publicity-team
this one. This is not supposed to be there
Copy code
fun TeamProfileScreen(
    navController: NavController,
    viewModel: TeamProfileViewModel,
    slug: String
) {
    LaunchedEffect(Unit) {
        viewModel.fetchTeamDetails(slug)
        viewModel.getTeamDetails(slug)
        viewModel.slugID.value = slug
    }
    val details = viewModel.teamDetails.value
    val tabs = listOf(
        TeamProfileTabs.About,
        TeamProfileTabs.Gallery,
        TeamProfileTabs.Members,
        TeamProfileTabs.Events
    )
    val profileNavController = rememberNavController()
    val navBackStackEntry by profileNavController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry?.destination?.route
    var selectedIndex by remember { mutableStateOf(0) }
    val clipboard = LocalClipboardManager.current
    val context = LocalContext.current
    val infoCardHeight = remember { mutableStateOf(0) }

    BackHandler(enabled = true) {
        viewModel.clearVars()
        viewModel.fetchTeamDetails(slug).cancel()
        viewModel.getTeamDetails(slug).cancel()
        navController.popBackStack()
    }

    CollapsingToolbarLayout(
        expandedHeight = 340.dp,
        expandedToolBarContent = {
            val members = viewModel.members.value
            Column(
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.SpaceEvenly,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(340.dp - infoCardHeight.value.toDp() / 10)
                    .graphicsLayer {
                        shape = OvalCustomShape()
                        clip = true
                    }
                    .background(
                        brush = Brush.verticalGradient(
                            colors = listOf(
                                Color(0xFF111422),
                                Color(0xFF567DF4)
                            )
                        )
                    )
                    .padding(top = 20.dp)
            ) {
                CoilImage(
                    imageModel = details?.teamInfo?.createdBy?.profile
                        ?: "<https://i.imgur.com/f4UVhDF.jpg>",
                    contentScale = ContentScale.Crop,
                    modifier = Modifier
                        .clip(CircleShape)
                        .size(100.dp),
                    placeHolder = ImageVector.vectorResource(id = R.drawable.ic_person),
                    error = ImageVector.vectorResource(id = R.drawable.ic_404_error)
                )
                Text(
                    text = details?.teamInfo?.createdBy?.name ?: "",
                    fontFamily = poppins,
                    fontWeight = FontWeight.Medium,
                    fontSize = 22.sp,
                    textAlign = TextAlign.Center,
                    color = TextWhite
                )

                Text(
                    text = details?.teamInfo?.createdBy?.email ?: "",
                    fontFamily = inter,
                    fontWeight = FontWeight.Medium,
                    fontSize = 16.sp,
                    textAlign = TextAlign.Center,
                    color = Color(0xFF48A7FF),
                    modifier = Modifier.pointerInput(Unit) {
                        detectTapGestures(onLongPress = {
                            clipboard.setText(AnnotatedString("<mailto:techteam@gst.sies.edu.in|techteam@gst.sies.edu.in>"))
                            Toast.makeText(context, "Copied to Clipboard", Toast.LENGTH_SHORT)
                                .show()
                        })
                    }
                )

                Row(
                    horizontalArrangement = Arrangement.Center,
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Icon(
                        painter = painterResource(id = R.drawable.linkedin_logo),
                        contentDescription = "Linkedin",
                        tint = Color.White
                    )
                    Spacer(modifier = Modifier.width(10.dp))
                    Icon(
                        painter = painterResource(id = R.drawable.github_logo),
                        contentDescription = "Github",
                        tint = Color.White
                    )
                }
                if (members?.isNotEmpty() ?: false) {
                    Spacer(
                        Modifier
                            .background(Color.Green)
                            .height(infoCardHeight.value.toDp())
                    )
                } else {
                    Spacer(
                        Modifier
                            .background(Color.Green)
                            .height(10.dp)
                    )
                }
            }
            if (members?.isNotEmpty() ?: false) {
                Card(
                    modifier = Modifier
                        .align(Alignment.BottomCenter)
                        .fillMaxWidth(0.75f)
                        .padding(bottom = 1.dp)
                        .onSizeChanged { infoCardHeight.value = it.height },
                    shape = RoundedCornerShape(50),
                    backgroundColor = ProfileOnBackground
                ) {
                    Row(
                        modifier = Modifier
                            .padding(vertical = 15.dp, horizontal = 5.dp)
                            .height(IntrinsicSize.Min),
                        horizontalArrangement = Arrangement.Center,
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        val imageSize = 48
                        val imageOffset = 34
                        Box(Modifier.width((imageSize + 2 * imageOffset).dp)) {
                            for (i in 0..if ((members?.size ?: 0) >= 3) 2 else if ((members?.size
                                    ?: 0) in listOf(1, 2)
                            ) members?.size!! - 1 else -1) {
                                CoilImage(
                                    imageModel = members?.get(i)?.member?.profile
                                        ?: "<https://i.imgur.com/f4UVhDF.jpg>",
                                    modifier = Modifier
                                        .absoluteOffset(x = ((2 - i) * imageOffset).dp)
                                        .border(
                                            width = 10.dp,
                                            color = ProfileOnBackground,
                                            shape = CircleShape
                                        )
                                        .size(imageSize.dp)
                                        .padding(1.dp)
                                        .clip(CircleShape),
                                    contentScale = ContentScale.Crop,
                                    placeHolder = ImageVector.vectorResource(id = R.drawable.ic_person),
                                    error = ImageVector.vectorResource(id = R.drawable.ic_404_error)
                                )
                            }
                        }
                        Spacer(modifier = Modifier.width(5.dp))
                        val no = members?.size?.minus(3) ?: 0
                        Text(
                            text = if (no > 1) "+ $no Members" else if (no == 1) "+ 1 Member" else "",
                            fontFamily = poppins,
                            fontWeight = FontWeight.Medium,
                            fontSize = 18.sp,
                            color = BrightBlue,
                            modifier = Modifier.wrapContentHeight()
                        )
                    }
                }
            }
            BackButton(
                navController = navController,
                modifier = Modifier
                    .align(Alignment.TopStart)
                    .statusBarsPadding()
                    .padding(12.dp),
                onClickAdditional = {
                    viewModel.clearVars()
                    viewModel.fetchTeamDetails(slug).cancel()
                }
            )
        },
        expandedToolbarColor = ProfileBackground,
        elevation = 0.dp,
        collapsedToolbarColor = ProfileBackground,
        collapsedHeight = 66.dp,
        collapsedToolBarContent = {
            Text(
                modifier = Modifier.align(Alignment.Center),
                text = "Profile",
                color = TopBarTitle,
                fontFamily = inter,
                fontWeight = FontWeight.Medium,
                fontSize = 22.sp
            )
        },
        dividerThickness = 1.dp,
        dividerColor = Divider,
        content = {
            item {
                ScrollableTabRow(
                    selectedTabIndex = selectedIndex,
                    backgroundColor = ProfileBackground,
                    indicator = {
                        TabRowDefaults.Indicator(
                            modifier = Modifier
                                .tabIndicatorOffset(it[selectedIndex])
                                .graphicsLayer {
                                    shape = RoundedCornerShape(12.dp)
                                    clip = true
                                },
                            height = 4.dp,
                            color = BrightBlue
                        )
                    },
                    divider = {
                        TabRowDefaults.Divider(
                            thickness = 2.dp,
                            color = Divider
                        )
                    }
                ) {
                    tabs.forEachIndexed { index, tab ->
                        if (currentRoute == tab.route) {
                            selectedIndex = index
                        }
                        CompositionLocalProvider(LocalRippleTheme.provides(NoRipple)) {
                            Tab(
                                selected = currentRoute == tab.route,
                                selectedContentColor = ProfileTabSelected,
                                unselectedContentColor = ProfileTabUnselected,
                                onClick = {
                                    profileNavController.navigate(route = tab.route) {
                                        profileNavController.currentDestination?.id?.let {
                                            popUpTo(it) {
                                                saveState = true
                                                inclusive = true
                                            }
                                        }
                                        launchSingleTop = true
                                        restoreState = true
                                    }
                                },
                                text = {
                                    Text(
                                        text = tab.name,
                                        fontFamily = inter,
                                        fontWeight = FontWeight.Medium,
                                        fontSize = 14.sp
                                    )
                                },
                            )
                        }
                    }

                }
            }
            item {
                TeamProfileTabNavigation(
                    navController = profileNavController,
                    mainNavController = navController,
                    viewModel = viewModel,
//                    recent = recent
                )
            }
        },
        modifier = Modifier.background(ProfileBackground)
    )
}
this is the UI code
also this issue doesn't occur if i fetch values direct from api this is only happening when I fetch values from room db
functions in viewModel
Copy code
fun fetchTeamDetails(slug: String) =
        viewModelScope.launch {
            repo.fetchTeamDetails(slug).collect()
        }

    fun getTeamDetails(slug: String) = viewModelScope.launch {
        repo.getTeamDetails(slug).collectLatest {
            Log.i("slug error", slug)
            Log.i("slug error", it.toString())
            teamDetails.value = it
            gallery.value = teamDetails.value?.teamInfo?.gallery
            members.value = teamDetails.value?.currentTeamMembers
            events.value = teamDetails.value?.upcomingEvents
            aboutTeam.value = HtmlCompat.fromHtml(
                teamDetails.value?.teamInfo?.body ?: "",
                HtmlCompat.FROM_HTML_MODE_COMPACT
            ).toString()
        }
    }
j
hmm are you sharing the viewmodel with other screens by any chance?
h
yes....
j
It's much easier to manage state if the ViewModel is bound to the screen and this screen only
i'm pretty positive that you have race conditions due to sharing viewmodel
just create a new viewmodel when navigating to particular team
viewmodel that is bound to the screen and that will be disposed right after the screen goes away
h
Ok thanks I'll try that
@Jakub Syty Thanks a lot that worked
👍 1