如何在MUI Menu中用Flexbox固定部分菜单项至底部
问题描述
我正在使用MUI的Menu组件,希望在菜单展开时将登出、用户详情等部分菜单项固定在屏幕底部。

以下是我的代码:
// MenuComponent.tsx import { Box, List, ListItemIcon, ListItemText, Menu, MenuItem, Typography, useMediaQuery, useTheme, } from "@mui/material"; import { styled } from "@mui/material/styles"; import React from "react"; import "./styles.css"; import { SvgArray } from "../../common/svgIcons"; import useMenuStore from "../../store/menuDataStore"; type MenuComponentProps = { anchorEl: HTMLElement | null; open: boolean; handleClose: () => void; menuItemId: string; menuItemName: string; }; const MenuComponent: React.FC<MenuComponentProps> = ({ anchorEl, open, handleClose, menuItemId, menuItemName, }) => { const theme = useTheme(); const is_heigth_less_800 = useMediaQuery("(max-height:800px)"); const is_heigth_less_900 = useMediaQuery("(max-height:900px)"); const is_heigth_less_1000 = useMediaQuery("(max-height:1000px)"); const is_between_lg_and_xl = useMediaQuery( theme.breakpoints.between("lg", "xl") ); const { menuItems } = useMenuStore(); const selectedMenuItem = menuItems.find((item) => item.id === menuItemId); console.log(selectedMenuItem); const isBelowLG = useMediaQuery(theme.breakpoints.down("lg")); const isHeightLess = useMediaQuery("(max-height:900px)"); const MenuContainerWrapper = styled(Box)({ display: "flex", flexDirection: "column", height: "100%", }); const LogoutContainerWrapper = styled(Box)({ marginTop: "auto", paddingBottom: 35, }); const MenuContainer = styled(Box)({ flexGrow: 1, overflowY: "auto", }); const LogoutContainer = styled(Box)({ // position: "fixed", // bottom: 0, // textAlign: "center", // paddingBottom: 35, // display: "flex", // flexDirection: "column", // alignItems: "center", //paddingTop: `calc(100vh - 0.5px)`, // paddingBottom: "calc(90vh-0.5px)", paddingTop: is_heigth_less_800 ? "170px" : is_heigth_less_900 ? "230px" : is_heigth_less_1000 ? "330px" : "500px", marginLeft: "8px", textAlign: "center", // paddingBottom: 35, }); const MenuItemContainer = styled(Box)({ display: "flex", flexDirection: "row", alignItems: "center", }); return ( <Menu id="basic-menu" MenuListProps={{ "aria-labelledby": "fade-button", }} sx={{ // height: "100vh", "& .MuiPopover-paper": { width: isBelowLG ? "280px" : is_between_lg_and_xl ? "300px" : "320px", height: "calc(100% - 0.1px) ", }, }} classes={{ paper: is_heigth_less_900 ? "menu-paper1" : "menu-paper" }} anchorOrigin={{ vertical: "center", horizontal: "left", }} anchorEl={anchorEl} open={open} onClose={handleClose} > <MenuContainerWrapper> <MenuContainer id="menucontainer"> <Typography id="menu-heading" variant="h6" sx={{ pl: 3, pr: 2, pt: 1, marginBottom: 4 }} > {menuItemName} </Typography> {selectedMenuItem?.subMenuItems?.map((item) => ( <MenuItem onClick={handleClose} key={item.id}> <MenuItemContainer sx={{ marginTop: "6px" }}> <ListItemIcon sx={{ color: theme.palette.primary.main, // marginBottom: "5px", marginLeft: "5px", "& svg": { fontSize: isHeightLess ? "24px" : "30px", }, }} > {/* <InboxOutlined style={{ flex: "0 0 30px", marginLeft: "3px" }} /> */} {React.createElement(SvgArray[item.icon], {})} </ListItemIcon> <Box sx={{ marginTop: "2px", paddingBottom: "15px" }}> <Typography variant="body1" style={{ flex: "1 1 auto", marginLeft: "10px" }} color={theme.palette.text.secondary} > {item.title} </Typography> </Box> </MenuItemContainer> </MenuItem> ))} {/* 注释掉的示例菜单项 */} </MenuContainer> <LogoutContainer id="logoutcontainer"> <MenuItem> <List sx={{ display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", }} > <ListItemText> <Typography variant="body1">Sam Smith</Typography> </ListItemText> <ListItemText> <Typography variant="body1">Admin</Typography> </ListItemText> <ListItemText> <Typography variant="body1">Abc Company</Typography> </ListItemText> </List> </MenuItem> <MenuItem> <Typography variant="body1" color={theme.palette.text.secondary}> Logout </Typography> </MenuItem> </LogoutContainer> </MenuContainerWrapper> </Menu> ); }; export default MenuComponent;
目前我通过断点设置padding的方式将登出容器的菜单项固定在屏幕底部,但该方法较为粗糙。请问是否可以在菜单内使用Flexbox优雅实现此需求?我尝试过一些方案但未成功,恳请帮助。
解决方案
你可以通过Flexbox的特性优雅实现底部固定效果,核心是利用margin-top: auto让底部容器自动推至菜单底部,同时确保父容器高度占满菜单面板。
步骤1:优化容器样式
删除依赖媒体查询设置padding的LogoutContainer,改用以下样式配置:
const MenuContainerWrapper = styled(Box)({ display: "flex", flexDirection: "column", height: "100%", padding: theme.spacing(1), // 可选,避免内容贴边 }); const MenuContainer = styled(Box)({ flexGrow: 1, overflowY: "auto", // 内容过多时自动滚动 marginBottom: theme.spacing(2), // 与底部区域保持间距 }); const LogoutContainer = styled(Box)({ marginTop: "auto", // 关键:自动占据剩余空间,推至底部 paddingBottom: theme.spacing(3), textAlign: "center", });
步骤2:调整菜单面板布局
确保菜单面板本身是Flex容器,让内部的MenuContainerWrapper能占满整个高度,修改Menu组件的sx配置:
<Menu id="basic-menu" MenuListProps={{ "aria-labelledby": "fade-button", }} sx={{ "& .MuiPopover-paper": { width: isBelowLG ? "280px" : is_between_lg_and_xl ? "300px" : "320px", height: "calc(100% - 0.1px)", display: "flex", // 设置为flex容器 flexDirection: "column", }, }} // 其他属性保持不变 > {/* 内部组件结构保持原有逻辑 */} </Menu>
关键说明
margin-top: auto:这是实现底部固定的核心,当父容器为flex-direction: column时,该属性会让元素自动占据剩余空间,从而固定在底部。- 滚动处理:
MenuContainer设置flexGrow:1和overflowY:auto,当菜单内容超过面板高度时,仅菜单区域滚动,底部登出区域始终固定。 - 自适应布局:无需依赖媒体查询设置padding,无论屏幕高度如何变化,底部区域都会自动适配菜单面板的高度。
内容的提问来源于stack exchange,提问作者Philomath
相关产品推荐
相关产品推荐

