androidkotlinandroid-jetpack-composekotlin-coroutinesandroid-jetpack-navigation

Can't send integer value from one screen to other


I am trying to send the Id of one of my object from one screen to other but I don't know why it is not getting the correct Id. When I seperately try to understand the navigation concept and made a small app it was working . That small app was like this

@Composable
fun ScreenOne(onClick : (Int) -> Unit){
    val number = 34
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { onClick(number) }) {
            Text(text = "Navigate")
        }
    }
}
@Composable
fun ScreenTwo(number:Int){
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(text = "Got number : $number")
    }
}
@Composable
fun MyApp(modifier: Modifier = Modifier){
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "a"){
        composable("a") { 
            ScreenOne {
                num->
                navController.navigate("b/${num}")
            }
        }
        composable("b/{num}") { 
            navBackStackEntry ->
            val number = navBackStackEntry.arguments?.getString("num")?.toIntOrNull() ?: 0
            ScreenTwo(number = number)
        }
    }
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            PracticingNavigationTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    MyApp(Modifier.padding(innerPadding))
                }
            }
        }
    }
}

But I don't know why this same logic is not working in my main project.

I will share my main project code files

  1. Profile Screen (The screen from which I was sharing the id) ->
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProfileScreen(
    onFabClicked : () -> Unit,
    onMailClicked: (Int) -> Unit,
    profileScreenViewModel: ProfileScreenViewModel
) {

    val TAG = "ProfileScreen"
    val mailList by profileScreenViewModel.mailLayoutList.observeAsState(emptyList())

    Log.d(TAG,"Size is : ${mailList.size}")
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
    val scope = rememberCoroutineScope()
    val bottomSheetState = rememberModalBottomSheetState(
        skipPartiallyExpanded = false

    )
    var showSheet by remember {
        mutableStateOf(false)
    }


    //Track if FAB is extended
    var isExtended by remember {
        mutableStateOf(false)
    }



    Box (
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectVerticalDragGestures { _, dragAmount ->
                    //Detect swipe up or down
                    if (dragAmount < 0) {
                        //Swiped Up
                        isExtended = true
                    } else if (dragAmount > 0) {
                        // Swiped down
                        isExtended = false
                    }
                }
            }
    ) {

        ModalNavigationDrawer(
            drawerState = drawerState, // Use ModalDrawer's state
            drawerContent = {
                DrawerItemUiLayout() // Drawer content
            },
            gesturesEnabled = true, // Allow gestures to open/close the drawer
            scrimColor = Color.Black.copy(alpha = 0.32f) // Scrim with dimming effect
        ) {

            Scaffold(
                modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
                topBar = {
                    TopBarOne(
                        openDrawer = {
                            scope.launch {
                                drawerState.open() // Open drawer with built-in animation
                            }
                        },
                        openProfile = {
                            showSheet = true

                        }
                    )
                },
                bottomBar = {
                    BottomBarLayout()
                },
                floatingActionButton = {
                    if (isExtended) {
                        Log.d("FAB","value is : $isExtended")
                        ExtendedFloatingActionButton(
                            onClick = { onFabClicked() },
                            modifier = Modifier.background(Color.Cyan)
                        ) {
                            Icon(imageVector = Icons.Default.Create, contentDescription = null)
                            Text(text = "Compose")
                        }
                    } else {
                        Log.d("FAB","value is : $isExtended")

                        FloatingActionButton(onClick = { onFabClicked() }) {
                            Icon(imageVector = Icons.Default.Create, contentDescription = null)
                        }
                    }
                }
            ) { innerPadding ->


                if (showSheet){
                    ModalBottomSheet(
                        modifier = Modifier.fillMaxHeight(),
                        sheetState = bottomSheetState,
                        onDismissRequest = { showSheet = false }
                    ) {
                        Text(
                            "Swipe up to open sheet. Swipe down to dismiss.",
                            modifier = Modifier.padding(16.dp)
                        )
                    }
                }
                LazyColumn(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(innerPadding)
                ) {

                    // Main content here
                    items(mailList){
                        mails ->
                        MailLayout(
                            mailLayoutItem = mails,
                            onMailClicked = {
                                onMailClicked(it)
                                Log.d(TAG, "Navigating to ShowMailScreen with id: ${it}")

                                            },
                            profileScreenViewModel = profileScreenViewModel
                        )
                    }



                }
            }

        }
    }

}
  1. ShowMailScreen(THis will receive the id)->
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ShowMailScreen(
    onBackButtonClicked: () -> Unit,
    onUnreadMailClicked: () -> Unit,
    profileScreenViewModel: ProfileScreenViewModel,
    mailId: Int
) {
    val TAG = "SHOW_MAIL_SCREEN"
    Log.d(TAG, "Got mail with id: ${mailId}")

    var mail : MailLayoutItem = MailLayoutItem(
        icon = 0,
        sender = "null",
        receiver = "null",
        subject = "null",
        content = "null",
        time = "00:00",
        isFavoriteClicked = false
    )
    LaunchedEffect(mailId) {
        mail = profileScreenViewModel.findAParticularMail(mailId)

    }

    var isStarClicked by remember { mutableStateOf(mail.isFavoriteClicked) }

    val example = listOf("1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")



    Scaffold(
        topBar = {
            TopBarThree(
                onBackButtonClicked = { onBackButtonClicked() },
                onMoreButtonClicked = { /*TODO*/ },
                onArchiveClicked = { /*TODO*/ },
                onDeleteClicked = { /*TODO*/ },
                onUnreadMailClicked = { onUnreadMailClicked() }
            )
        },
        bottomBar = { BottomBarLayout()}
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(innerPadding)
        ) {
            // LazyColumn taking 3/4th of the screen
            LazyColumn(
                modifier = Modifier
                    .fillMaxWidth()
                    .weight(3f)
            ) {
                item { 
                    Text(text = "GOt mail with name ${mail.subject}")
                }
                item {
                    Row (
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(16.dp),
                        horizontalArrangement = Arrangement.SpaceBetween
                    ){
                        Text(text = "Your ebook bargains for Tuesday.", fontSize = 26.sp, modifier = Modifier.weight(3f))
                        IconButton(onClick = {
                            profileScreenViewModel.ToggleFavorite(mailId = mail.id)
                            isStarClicked = !isStarClicked
                        }) {
                            if (isStarClicked){
                                Icon(
                                    imageVector = Icons.Filled.Star,
                                    contentDescription = null,
                                    tint = Color.Blue
                                )
                            }else{
                                Icon(painter = painterResource(id = R.drawable.baseline_star_border_24) , contentDescription = null )
                            }
                        }
                    }
                }
                items(example) { item: String ->
                    Text(text = item)
                }
                stickyHeader {
                    Text(text = "This is the second part", fontWeight = FontWeight.Bold)
                }
            }

            ReplyRowLayout()
        }
    }
}

  1. AppNavigation
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun AppNavigation(profileScreenViewModel: ProfileScreenViewModel = viewModel(
)){
    val navController = rememberNavController()

    NavHost(navController = navController, startDestination = Screens.PrimaryScreen.route){
        composable(route = Screens.PrimaryScreen.route){

            ProfileScreen(
                onFabClicked = { navController.navigate(Screens.ComposeScreen.route) },
                onMailClicked = {
                    mailId->

                    navController.navigate("${Screens.ShowMailScreen.route}/{mailId}")
                                },
                profileScreenViewModel = profileScreenViewModel
            )
        }
        composable(route = Screens.ComposeScreen.route) {
            ComposeScreen(
                onBackButtonClicked = {navController.navigateUp()},
                onSendButtonClicked = {navController.navigateUp()},
                profileScreenViewModel = profileScreenViewModel
            )

        }
        composable(
            route = "${Screens.ShowMailScreen.route}/{mailId}"
        ) {navBackStackEntry->
            val mailId = navBackStackEntry.arguments?.getString("mailId")?.toIntOrNull() ?: 0

            ShowMailScreen(
                onBackButtonClicked = {navController.navigateUp()},
                onUnreadMailClicked = {navController.navigateUp()},
                profileScreenViewModel = profileScreenViewModel,
                mailId = mailId

            )
        }
    }
}
  1. ViewModel ->
class ProfileScreenViewModel(
    private val allMailsRepository: MailItemRepository = MailItemGraph.allMailsRepository
) : ViewModel() {
    private val TAG = "ProfileScreenViewModel"

    private val getDateAndTime = GetDateAndTime()

    private val _mailLayoutList = MutableLiveData<List<MailLayoutItem>>(
        emptyList()
    )

    val mailLayoutList: LiveData<List<MailLayoutItem>> get() = _mailLayoutList

    init {
        viewModelScope.launch {
            allMailsRepository.getAllMails().collect { mails ->
                _mailLayoutList.value = mails.sortedByDescending { it.id }

            }
        }
    }


    //Selecting favorite or not Favorite
    fun ToggleFavorite(mailId: Int) {
        _mailLayoutList.value = _mailLayoutList.value?.map { mailLayoutItem: MailLayoutItem ->
            if (mailLayoutItem.id == mailId) {
                val updatedMailLayoutItem = mailLayoutItem.copy(
                    isFavoriteClicked = !mailLayoutItem.isFavoriteClicked
                )
                viewModelScope.launch {
                    try {
                        allMailsRepository.insertMail(updatedMailLayoutItem)
                    } catch (e: Exception) {
                        Log.d(TAG, "Can't update the mail : ${e.message}")
                    }
                }

                updatedMailLayoutItem
            } else {
                mailLayoutItem
            }
        }
    }

    //Sending the clicked mail to other screen to view full mail
    fun findAParticularMail(mailId: Int): MailLayoutItem {
        var foundMail: MailLayoutItem = MailLayoutItem(
            icon = 0,
            sender = "null",
            receiver = "null",
            subject = "null",
            content = "null",
            time = "00:00",
            isFavoriteClicked = false
        )
        viewModelScope.launch {
            try {
                allMailsRepository.getSpecificMail(mailId).collect {
                    foundMail = it
                }
            } catch (e: Exception) {
                Log.d(TAG, "Specific mail can't be found with error : ${e.message}")
            }
        }
//        val mail = _mailLayoutList.value?.find {
//            foundMail->
//            foundMail.id == mailId
//        }
        return foundMail
    }

    //Adding new mail in list
    @RequiresApi(Build.VERSION_CODES.O)
    fun addNewMail(mailLayoutItem: MailLayoutItem) {
        val currentDateAndTime = LocalDateTime.now(ZoneId.of("Asia/Kolkata"))
        val formattedTimeOrDate = getDateAndTime.getFormatedTimeOrDate(currentDateAndTime)
        val newMailLayoutItem = mailLayoutItem.copy(time = formattedTimeOrDate)
        viewModelScope.launch {
            try {
                allMailsRepository.insertMail(newMailLayoutItem)
            } catch (e: Exception) {
                Log.d(TAG, "Error occurred while inserting the mail: ${e.message}")
            }
        }
    }
}
  1. MailLayout(A composable important to send the id and used in profile screen)->
@Composable
fun MailLayout(
    mailLayoutItem: MailLayoutItem,
    onMailClicked: (Int) -> Unit,
    profileScreenViewModel: ProfileScreenViewModel
){
    val TAG = "MAIL_LAYOUT_SCREEN"

    fun truncateString(input:String, maxLength:Int):String{
        if (input.length > maxLength){
            return input.take(maxLength) + "..."
        }else{
            return input
        }
    }
    
    val mailImageBitmap = ImageBitmap.imageResource(id = mailLayoutItem.icon)

    Row (
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp)
            .clickable {
                onMailClicked(
                    mailLayoutItem.id
                )
                Log.d(TAG, "Navigating to ShowMailScreen with id: ${mailLayoutItem.id}")

            },
        horizontalArrangement = Arrangement.Start,
        verticalAlignment = Alignment.CenterVertically
    ){
        Box(modifier = Modifier
            .size(60.dp)
            .clip(CircleShape)
            ){
            Image(bitmap = mailImageBitmap, contentDescription = null, contentScale = ContentScale.Fit)
        }

        Column(
            modifier = Modifier.fillMaxHeight().padding(horizontal = 8.dp).weight(5f),
        ) {
            Text(text = truncateString(mailLayoutItem.sender, 25), fontWeight = FontWeight.Bold)
            Text(text = truncateString(mailLayoutItem.subject, 50), fontSize = 12.sp, fontWeight = FontWeight.Bold)
            Text(text = truncateString(mailLayoutItem.content, 40), fontSize = 12.sp)
        }
        Column(
            modifier = Modifier.fillMaxHeight(),
            verticalArrangement = Arrangement.SpaceBetween
        ) {
            Text(text = mailLayoutItem.time, fontSize = 10.sp, fontWeight = FontWeight.ExtraBold)
            IconButton(onClick = {
                profileScreenViewModel.ToggleFavorite(mailId = mailLayoutItem.id)
            }) {
                if (mailLayoutItem.isFavoriteClicked){
                    Icon(imageVector = Icons.Filled.Star, contentDescription = null, tint = Color.Blue)
                }else{
                    Icon(painter = painterResource(id = R.drawable.baseline_star_border_24), contentDescription = null)
                }
            }
        }
    }

}

  1. Data Class
@Entity
data class MailLayoutItem(
    @PrimaryKey(autoGenerate = true)
    val id : Int = 0,
    val icon : Int,
    val sender : String,
    val receiver : String,
    val subject : String,
    val content : String,
    val time : String,
    val isFavoriteClicked : Boolean
)

I used some logging, so I am sharing some important log entries regarding this issue "

  1. 2024-09-22 11:11:57.657 12290-12290 ProfileScreen com.example.gmailappclone D Navigating to ShowMailScreen with id: 2

  2. 2024-09-22 11:11:57.658 12290-12290 MAIL_LAYOUT_SCREEN com.example.gmailappclone D Navigating to ShowMailScreen with id: 2

  3. 2024-09-22 11:11:57.703 12290-12290 SHOW_MAIL_SCREEN com.example.gmailappclone D Got mail with id: 0

"

I humbly request you to please help me regarding why the navigation logic is not working in my project.


Solution

  • It seems that you forgot the dollar sign in front of mailId here, so you are not passing the actual mailId to the destination:

    onMailClicked = { mailId->
      navController.navigate("${Screens.ShowMailScreen.route}/{mailId}")
    }