Home > other >  Jetpack Compose : Hoisitng click state from DropDownItems to DropDownMenu
Jetpack Compose : Hoisitng click state from DropDownItems to DropDownMenu

Time:11-06

What i am trying to do is to close the TopBar dropdown menu after clicking the dropdown item. It can be easily done, if i am putting the dropdown items directly inside the dropdown menu. But here i am trying to separate it as a composable for readability.

Here is my TopAppBar

@Composable
fun TopBar(
    scope: CoroutineScope,
    scaffoldState: ScaffoldState,
    event: (AdminLaunchEvents) -> Unit,
    navController: NavHostController
) {
    val openDialog = remember { mutableStateOf(false) }
    TopAppBar(
        title = {
            Text(text = "Main App Admin Area", fontSize = 18.sp)
        },
        actions = {
            OverflowMenu() {
                SettingsDropDownItem(onClick = {})
                ModeDropDownItem(onClick = {})
                LogoutDropDownItem(onClick = {
                    openDialog.value = true
                })
            }
        },
        backgroundColor = MaterialTheme.colors.primary,
        contentColor = Color.White
    )
    if (openDialog.value) {
        LogOutComponent(openDialog = openDialog, event = event,navController = navController)
    }
}

And this is the OverFlowMenu composable which contains the DropDown Menu

@Composable
fun OverflowMenu(content: @Composable () -> Unit) {
    var showMenu by remember { mutableStateOf(false) }

    IconButton(onClick = {
        showMenu = !showMenu
    }) {
        Icon(
            imageVector = Icons.Outlined.MoreVert,
            contentDescription = "More",
        )
    }
    DropdownMenu(
        expanded = showMenu,
        onDismissRequest = { showMenu = false }
    ) {
        content()        
    }
}

Now given below is the DropDownItem.

@Composable
fun SettingsDropDownItem(onClick: () -> Unit) {
    DropdownMenuItem(onClick = onClick) {
        Icon(
            Icons.Filled.Settings,
            contentDescription = "Settings",
            modifier = Modifier.size(24.dp)
        )
        Spacer(modifier = Modifier.width(8.dp))
        Text("Settings")
    }
}

What i am trying to do is, when i click the SettingsDroDownItem, i need to capture the click event in the OverFlowMenu composable to make the showMenu false, so as the hide the DropdownMenu. I can get the click event in the TopAppBar, but how to get it on DropDownMenu.

How to do that?

CodePudding user response:

  1. The first option is moving showMenu state out of OverflowMenu, as this is not the only composable which depends on the value. Something like this: OverFlowMenu:

    @Composable
    fun OverflowMenu(showMenu: Bool, setShowMenu: (Bool) -> Unit, content: @Composable () -> Unit) {
        // ...
    }
    

    TopBar:

    actions = {
        var (showMenu, setShowMenu) = remember { mutableStateOf(false) }
        OverflowMenu(showMenu, setShowMenu) {
            SettingsDropDownItem(onClick = {
                openDialog.value = true
                setShowMenu(false)
            })
        }
    },
    
  2. An other options is creating something like OverflowMenuScope, and running SettingsDropDownItem on this scope so it can close the menu itself: OverflowMenu:

    interface OverflowMenuScope {
        fun closeMenu()
    }
    
    @Composable
    fun OverflowMenu(content: @Composable OverflowMenuScope.() -> Unit) {
        var showMenu by remember { mutableStateOf(false) }
        val scope = remember {
            object: OverflowMenuScope {
                override fun closeMenu() {
                    showMenu = false
                }
            }
        }
        //...
        DropdownMenu(
            expanded = showMenu,
            onDismissRequest = { showMenu = false }
        ) {
            scope.content()
        }
    }
    

    SettingsDropDownItem:

    @Composable
    fun OverflowMenuScope.SettingsDropDownItem(onClick: () -> Unit) {
        DropdownMenuItem(onClick = {
            closeMenu()
            onClick()
        }) {
            Icon(
                Icons.Filled.Settings,
                contentDescription = "Settings",
                modifier = Modifier.size(24.dp)
            )
            Spacer(modifier = Modifier.width(8.dp))
            Text("Settings")
        }
    }
    
  • Related