Search code examples
androidkotlinandroid-jetpack-composeandroid-jetpack-navigation

How can I navigate to a new screen with varied data from a lazy vertical grid in Jetpack Compose?


Subject Section

As shown in the above image, I want to navigate to subject screen from this home screen. But I want data of that subject to load into new screen when clicked on each subjects.

Right now, it leads me to same subject when I click on any item of lazy vertical grid.

Subject Screen(Biology) Subject Screen(Physics)

Home Screen[Subject Section]

@Composable
fun SubjectSection(onClick: () -> Unit, modifier: Modifier = Modifier) {


    Text(
        text = "Subjects",
        style = MaterialTheme.typography.titleMedium
    )

    Spacer(modifier = Modifier.height(8.dp))

    LazyVerticalGrid(
        columns = GridCells.Fixed(2),
        //contentPadding = PaddingValues(8.dp)
    ) {
        items(subjectList, key = { it.id }) { subject ->
            SubjectBox(subject = subject) {
                onClick()
            }
        }
    }
}


@Composable
fun SubjectBox(subject: Subject, onClick: () -> Unit) {
    Box(
        modifier = Modifier
            .padding(end = 8.dp, bottom = 8.dp)
            .background(subject.color, RoundedCornerShape(8.dp))
            .width(140.dp)
            .height(40.dp)
            .clickable { onClick() }
    ) {
        TextButton(
            onClick = onClick,
            modifier = Modifier.align(Alignment.CenterStart)
        ) {
            Text(
                text = subject.name,
                style = MaterialTheme.typography.bodyMedium,
                modifier = Modifier
                //.padding(start = 5.dp)
                //.align(Alignment.CenterStart)
            )
        }

        Image(
            painter = painterResource(id = subject.image),
            contentDescription = null,
            modifier = Modifier
                .size(60.dp)
                .padding(end = 5.dp)
                .align(Alignment.CenterEnd)
        )
    }
}

Function Call

SubjectSection(onClick = { navController.navigate(Screen.SubjectPhysics.route) })

Subject Screen

@Composable
fun SubjectScreen(
    subjects: Subjects,
    categories: List<ChapterCategory>,
    modifier: Modifier = Modifier
) {

    Column {
        Box(
            modifier = modifier
                .wrapContentSize(align = TopCenter)
                .background(color = Color(0xFFF9E9FE))
        )
        {
            Column {
                HeaderSection {
                }
                SubjectNameBox(subjects)
            }
        }
        Box(modifier = modifier.fillMaxSize()) {
            ChapterColumn(categories = categories)
        }


    }


}

@Composable
fun HeaderSection(
    modifier: Modifier = Modifier,
    onBack: () -> Unit
) {
    Row(
        modifier = modifier
            .fillMaxWidth()
            .padding(start = 0.dp, end = 20.dp, top = 20.dp, bottom = 20.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {

        IconButton(onClick = onBack) {
            Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null)
        }
        Icon(
            imageVector = Icons.Default.Search,
            contentDescription = null,
            modifier = Modifier.align(CenterVertically)
        )
    }


}

@Composable
fun SubjectNameBox(subjects: Subjects, modifier: Modifier = Modifier) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(20.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(
            text = subjects.name,
            style = MaterialTheme.typography.headlineLarge
        )
        Image(
            painter = painterResource(id = subjects.iconResId), contentDescription = null,
            modifier = Modifier.size(80.dp)
        )
    }

}

@Composable
fun ChapterBox(
    modifier: Modifier = Modifier,
    chapterName: ChapterName
) {

    Box(
        modifier = modifier
            .fillMaxSize()
            .padding(2.dp)
            .background(color = Color(0xFF8F6F5FA), RoundedCornerShape(8.dp))
    )
    {
        Row(
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(16.dp),
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp)
        ) {

            Image(painter = painterResource(id = chapterName.icon), contentDescription = null)
            Column {
                Text(text = chapterName.name, maxLines = 1, overflow = TextOverflow.Ellipsis)
                Spacer(Modifier.height(4.dp))
                Text(
                    text = chapterName.description,
                    style = MaterialTheme.typography.bodyMedium,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis
                )
            }
        }

    }

}


@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ChapterColumn(
    categories: List<ChapterCategory>,
    modifier: Modifier = Modifier
) {


    LazyColumn() {

        categories.forEach { category ->
            stickyHeader {
                ChapterHeader(category.name)
            }
            items(category.chapters) { chapter ->
                ChapterBox(chapterName = chapter)
            }
        }
    }

}

@Composable
fun ChapterHeader(
    category: String,
    modifier: Modifier = Modifier
) {
    Text(
        text = category,
        fontSize = 16.sp,
        fontWeight = FontWeight.Bold,
        modifier = modifier
            .fillMaxWidth()
            .background(MaterialTheme.colorScheme.primaryContainer)
            .padding(16.dp)
    )
}

Subjects sealed class

sealed class Subjects(val name: String, val iconResId: Int, val chapters: List<ChapterName>)


object Chemistry : Subjects(" Chemistry", R.drawable.chemistry, chapterNameChemistryOrganic)
object Biology : Subjects(" Biology", R.drawable.biology, chapterNameChemistryOrganic)
object Physics : Subjects(" Physics", R.drawable.physics3, chapterNameChemistryOrganic)


object Mat : Subjects("MAT", R.drawable.maths1, chapterNameMat)
object MockTest : Subjects("Mock Test", R.drawable.maths1, chapterNameMockTest)

Chapter Category data class

data class ChapterCategory(
    val name: String,
    val chapters: List<ChapterName>
)


val allChaptersChemistry = listOf(
    ChapterCategory("Physical Chemistry", chapterNameChemistryPhysical),
    ChapterCategory("Organic Chemistry", chapterNameChemistryOrganic),
    ChapterCategory("Inorganic Chemistry", chapterNameChemistryInorganic),
)

val allChaptersBiology = listOf(
    ChapterCategory(name = "Zoology", chapters = chapterNameBiologyZoology),
    ChapterCategory(name = "Botany", chapters = chapterNameBiologyBotany)
)

val allChaptersPhysics = listOf(
    ChapterCategory(name = "Mechanics", chapters = chapterNamePhysicsMechanics),
    ChapterCategory(name = "Thermodynamics", chapters = chapterNamePhysicsThermodynamics),
    ChapterCategory(name = "Sound", chapters = chapterNamePhysicsSound),
    ChapterCategory(name = "Optics", chapters = chapterNamePhysicsOptics),
    ChapterCategory(name = "Electrostatics", chapters = chapterNamePhysicsElectrostatics),
    ChapterCategory(
        name = "Electricity And Magnetism",
        chapters = chapterNamePhysicsElectricityAndMagnetism
    ),
    ChapterCategory(name = "Modern Physics", chapters = chapterNamePhysicsModernPhysics),
)

Navigation

@Composable
fun NavigationScreen() {

    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = Screen.Navigation.route
    ) {
        composable(route = Screen.Home.route) {
            HomeScreen(navController)
        }

        composable(route = Screen.Navigation.route) {
            BottomNavigationScreen(navController)
        }


        composable(route = Screen.SubjectPhysics.route) {
            SubjectScreen(subjects = Physics, categories = allChaptersPhysics)
        }
        composable(route = Screen.SubjectChemistry.route) {
            SubjectScreen(subjects = Chemistry, categories = allChaptersChemistry)
        }
        composable(route = Screen.SubjectBiology.route) {
            SubjectScreen(subjects = Biology, categories = allChaptersBiology)
        }

Solution

  • You are navigating to same subject screen because of you are calling the same logic on all the item click as below.

    Problem You are passing nothing here on ItemClick. so it leads to same page for all items.

    items(subjectList, key = { it.id }) { subject ->
                SubjectBox(subject = subject) {
                    onClick()
                }
            }
    

    Function Call

    SubjectSection(onClick = { navController.navigate(Screen.SubjectPhysics.route) })
    

    Solution So what you can do is send some variable or value which will be used to distinguish the subjects.

    So you can define a unique key for subject name and on which you can filter the page route.

    items(subjectList, key = { it.id }) { subject ->
                    SubjectBox(subject = subject) {
                        onClick(subject.name)
                    }
                }
    

    and on function call you add when or if and set the route as per that.

    SubjectSection(onClick = {route ->
            if(route == "Chemistry") {
                navController.navigate(Screen.SubjectChemistry.route)
            }else if(route == "Biology"){
                navController.navigate(Screen.SubjectBiology.route)
            }else{
                navController.navigate(Screen.SubjectPhysics.route)
            }
        })