Search code examples
androidkotlinlambdaandroid-jetpack-composefunction-parameter

How to pass a Composable to another Composable as its parameter and display/run it in Jetpack Compose


I have this drawer I learned to make on youtube

https://www.youtube.com/watch?v=JLICaBEiJS0&list=PLQkwcJG4YTCSpJ2NLhDTHhi6XBNfk9WiC&index=31 Philipp Lackner

SwipeScreen enter image description here

I want to add the drawer to my app which has multiple screens and some of them don't need the drawer so I implemented navigation with screens , and some of the screens need to also have the drawer ontop of them wrap them.

this is the code of the drawer


val scaffoldState = rememberScaffoldState()
                val scope = rememberCoroutineScope()
                Scaffold(
                    drawerGesturesEnabled = scaffoldState.drawerState.isOpen,
                    scaffoldState = scaffoldState, topBar = {
                    AppBar(onNavigationIconClick = {
                        scope.launch {
                            scaffoldState.drawerState.open()
                        }

                    })
                }, drawerContent = {
                    DrawerHeader()
                    DrawerBody(items = listOf(
                        MenuItem(
                            id = "home",
                            title = "Home",
                            contentDescription = "Go to home screen",
                            icon = Icons.Default.Home
                        ),
                        MenuItem(
                            id = "settings",
                            title = "Settings",
                            contentDescription = "Go to Settings screen",
                            icon = Icons.Default.Settings
                        ),
                        MenuItem(
                            id = "help",
                            title = "Help",
                            contentDescription = "Go to help screen",
                            icon = Icons.Default.Info
                        ),
                    ), onItemClick = {
                        println("Clicked on ${it.title}")

                        when (it.id) {
                            "home" -> {
                                println("Clicked on ${it.title}")
                            }
                            "settings" -> {
                                println("Clicked on ${it.title}")
                            }
                            "help" -> {
                                println("Clicked on ${it.title}")
                            }
                        }
                    })

                }) {

                    Text(text = "Hello World")

                }

the Text = Hellow world is where I want to pass my parameter of the screen which I don't know how to do it. I want to add a parameter that takes a composable function and runs it inside

and I followed this navigation video on how to navigate in kotlin

https://www.youtube.com/watch?v=4gUeyNkGE3g&list=PLQkwcJG4YTCSpJ2NLhDTHhi6XBNfk9WiC&index=18

which has 3 big files so if you ask I post them here but I will try to be more specific about what is needed

composable(route = Screen.RegisterScreen.route) {
            RegisterScreen(navController = navCotroller)
        }

and if I put the code in the drawer it works well but I want to split the code to be cleaner because I use the drawer in more places

the code work like the example bellow

composable(route = Screen.PreferenceScreen.route) {
            val scaffoldState = rememberScaffoldState()
            val scope = rememberCoroutineScope()
            Scaffold(

                drawerGesturesEnabled = scaffoldState.drawerState.isOpen,
                scaffoldState = scaffoldState,
                topBar = {
                    AppBar(onNavigationIconClick = {
                        scope.launch {
                            scaffoldState.drawerState.open()
                        }

                    })
                },
                drawerContent = {
                    DrawerHeader()
                    DrawerBody(items = listOf(
                        MenuItem(
                            id = "swipe",
                            title = "Swipe",
                            contentDescription = "Go to Swipe screen",
                            icon = Icons.Default.Home
                        ),
                        MenuItem(
                            id = "settings",
                            title = "Settings",
                            contentDescription = "Go to Settings screen",
                            icon = Icons.Default.Settings
                        ),
                        MenuItem(
                            id = "profile",
                            title = "Profile",
                            contentDescription = "Go to profile screen",
                            icon = Icons.Default.Info
                        ),
                    ), onItemClick = {
                        when (it.id) {
                            "swipe" -> {
                                navCotroller.navigate(Screen.SwipeScreen.route)
                            }
                            "settings" -> {
                                navCotroller.navigate(Screen.PreferenceScreen.route)
                            }
                            "profile" -> {
                                navCotroller.navigate(Screen.CelebProfileScreen.route)
                            }
                        }
                    })

                }) {

         ----->        PreferenceScreen(navController = navCotroller)

            }

        }

but it is not clean code!! how can I use a function pointer to make this work ??

enter image description here


Solution

  • hey guys if you want to know how I solved to problem in my own app see this code

    end result

    enter image description here

    https://github.com/dolev146/Politi-Cal/tree/main/app/src/main/java/com/example/politi_cal

    this is the navigation file I just used the concept described in the answers to pass the screens as a parameter

    something like that where the drawer wrap the content screen

     composable(route = Screen.AdminOnlyScreen.route) {
                DrawerTopBar(navController = navCotroller, screen = { navController ->
                    // this is the screen that will be drawn after the drawer
                    // swipe screen
                    AdminOnlyScreen(navController = navController, auth = auth)
    
                })
            }
    
            composable(route = Screen.UserAnalyticsScreen.route) {
                DrawerTopBar(navController = navCotroller, screen = { navController ->
                    // this is the screen that will be drawn after the drawer
                    // swipe screen
    
    
                    UserAnalyticsScreen(
                        navController = navController,
                        auth = auth
    
                    )
    
                })
            }
    
    

    and the drawer looks like https://github.com/dolev146/Politi-Cal/tree/main/app/src/main/java/com/example/politi_cal/screens/NavDrawer

    // the drawer TopBar should get the navController as a parameter ,
    // and the composable screen function that it needs to draw after the drawer
    @Composable
    fun DrawerTopBar(
        navController: NavController,
        auth: FirebaseAuth = FirebaseAuth.getInstance(),
        screen: @Composable (navController: NavController) -> Unit
    ) {
        var isAdminState by remember {
            mutableStateOf(false)
        }
    
    
        val scaffoldState = rememberScaffoldState()
        val scope = rememberCoroutineScope()
        val context = LocalContext.current
        isAdmin(context) {
            isAdminState = true
        }
        var menuContent = mutableListOf(
            MenuItem(
                id = "search",
                title = "Search",
                contentDescription = "Go to Search screen",
                icon = Icons.Default.Search
            ),
    
    //        MenuItem(
    //            id = "swipe",
    //            title = "Swipe",
    //            contentDescription = "Go to Swipe screen",
    //            icon = Icons.Default.Home
    //        ),
    //        MenuItem(
    //            id = "preferences",
    //            title = "Preferences",
    //            contentDescription = "Go to Preferences screen",
    //            icon = Icons.Default.Settings
    //        ),
    //        MenuItem(
    //            id = "logout",
    //            title = "Logout",
    //            contentDescription = "Go to Logout screen",
    //            icon = Icons.Default.ExitToApp
    //        ),
    //        MenuItem(
    //            id = "user analytics",
    //            title = "User Analytics",
    //            contentDescription = "Go to User Analytics screen",
    //            icon = Icons.Default.MoreVert
    //        )
    
    
        )
    
        if (isAdminState) {
    
            menuContent.add(
                MenuItem(
                    id = "user analytics",
                    title = "Analytics",
                    contentDescription = "Go to User Analytics screen",
                    icon = Icons.Default.MoreVert
                )
            )
    
    
    
            menuContent.add(
                MenuItem(
                    id = "add celeb",
                    title = "Add Celeb",
                    contentDescription = "Go to User profile screen",
                    icon = Icons.Default.Add
                )
            )
            menuContent.add(
                MenuItem(
                    id = "admin analytics",
                    title = "Admin Analytics",
                    contentDescription = "Go to Admin Analytics screen",
                    icon = Icons.Default.MoreVert
                )
            )
            menuContent.add(
                MenuItem(
                    id = "admin user management",
                    title = "User Search",
                    contentDescription = "Go to Admin User Management screen",
                    icon = Icons.Default.MoreVert
                )
            )
    
    
    
    
    
    
    
    
    
    
    
            menuContent.add(
                MenuItem(
                    id = "logout",
                    title = "Logout",
                    contentDescription = "Go to Logout screen",
                    icon = Icons.Default.ExitToApp
                )
            )
    
    
        } else {
            // this is user menu
            menuContent.add(
                MenuItem(
                    id = "swipe",
                    title = "Swipe",
                    contentDescription = "Go to Swipe screen",
                    icon = Icons.Default.Home
                )
            )
    
            menuContent.add(
                MenuItem(
                    id = "user analytics",
                    title = "Analytics",
                    contentDescription = "Go to User Analytics screen",
                    icon = Icons.Default.MoreVert
                )
            )
    
            menuContent.add(
                MenuItem(
                    id = "preferences",
                    title = "Preferences",
                    contentDescription = "Go to Preferences screen",
                    icon = Icons.Default.Settings
                )
            )
    
            menuContent.add(
                MenuItem(
                    id = "my profile",
                    title = "My Profile",
                    contentDescription = "Go to User profile screen",
                    icon = Icons.Default.Person
                )
            )
    
    
    
    
    
    
    
    
    
    
    
    
    
    
            menuContent.add(
                MenuItem(
                    id = "logout",
                    title = "Logout",
                    contentDescription = "Go to Logout screen",
                    icon = Icons.Default.ExitToApp
                )
            )
    
    
        }
    
    
    
        Scaffold(
    
            drawerGesturesEnabled = scaffoldState.drawerState.isOpen,
            scaffoldState = scaffoldState,
            topBar = {
                AppBar(onNavigationIconClick = {
                    scope.launch {
                        scaffoldState.drawerState.open()
                    }
    
                })
            },
            drawerContent = {
                DrawerHeader()
                DrawerBody(items = menuContent, onItemClick = {
                    when (it.id) {
                        "swipe" -> {
                            navController.navigate(Screen.SwipeScreen.route)
                        }
                        "preferences" -> {
                            navController.navigate(Screen.PreferenceScreen2.route)
                        }
                        "user profile" -> {
                            navController.navigate(Screen.UserProfileScreen.route)
                        }
                        "add celeb" -> {
                            navController.navigate(Screen.AddCelebScreen.route)
                        }
                        "user analytics" -> {
                            navController.navigate(Screen.UserAnalyticsScreen.route)
                        }
                        "logout" -> {
    //                        auth.signOut()
                            navController.navigate(Screen.SplashScreen.route)
                        }
                        "search" -> {
                            navController.navigate(Screen.SearchScreen.route)
                        }
                        "my profile" ->{
    
                            navController.navigate(Screen.UserProfileScreen.route)
                        }
    
    
                        "admin only" -> {
                            // check that the user is admin
                            // if not, show a message
                            // if yes, navigate to the admin only screen
                            if (isAdminState) {
                                navController.navigate(Screen.AdminOnlyScreen.route)
                            } else {
                                // show a message
                                Toast.makeText(context, "You are not an admin", Toast.LENGTH_SHORT)
                                    .show()
                            }
                        }
                        "admin analytics" -> {
                            navController.navigate(Screen.AdminAnalyticsMenuScreen.route)
                        }
                        "admin user management" -> {
                            navController.navigate(Screen.AdminUserManagementScreen.route)
                        }
    
    
                    }
                })
    
            }) {
    
            screen(navController = navController)
    
        }
    }
    

    watch the full code in the link and please upvote if you liked my solution :) have fun coding