You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 09:24:57