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

如何用flexbox实现MUI Tab单个居中/多个均分空间?

MUI Tab 布局适配方案

问题场景

需要实现MUI Tab组件的两种布局逻辑:

  • 当Tab数量多于1个时,所有Tab均分可用宽度
  • 仅存在1个Tab时,Tab在容器中水平居中
    当前使用flex布局实现时,单个或两个Tab的场景下布局不符合预期。

原代码

import type { ReactElement } from 'react';
import { useState } from 'react';

import { Box, Tab, Typography, useMediaQuery, useTheme } from '@mui/material';
import Tabs, { tabsClasses } from '@mui/material/Tabs';

const MOCK_TAB_VALUES = [
    { month: 'August 2023', date: '20.08.2023' },
    // { month: 'September 2023', date: '15.09.2023' },
    // { month: 'October 2023', date: '20.10.2023' },
    // { month: 'November 2023', date: '15.11.2023' },
    // { month: 'December 2023', date: '15.11.2023' },
];

export const FormTabComponent = (): ReactElement => {
    const [value, setValue] = useState<number>(0);
    const theme = useTheme();
    const isLargeScreen = useMediaQuery(theme.breakpoints.up('lg'));

    const handleTabChange = (event, newValue) => {
        setValue(newValue);
    };
    const isDarkMode = theme.palette.mode === 'dark';
    return (
      <Tabs
        sx={{
                width: 1,
                position: 'relative',
                height: '60px',
                justifyContent: 'space-evenly',
                [`& .${tabsClasses.scrollButtons}`]: {
                    '&.Mui-disabled': { opacity: 0, width: 0 },
                },
                [`& .MuiTabScrollButton-root`]: {
                    opacity: 1,
                },
                '& .MuiTabScrollButton-root:first-child': {
                    zIndex: 1,
                    position: 'absolute',
                    background: isDarkMode
                        ? 'linear-gradient(90deg, #222221, transparent)'
                        : 'linear-gradient(90deg, #FDFDFD, transparent)',
                    '& .MuiSvgIcon-root': {
                        width: '24px',
                        height: '24px',
                    },
                },
                '& .MuiTabScrollButton-root:last-child': {
                    position: 'absolute',
                    right: 0,
                    background: isDarkMode
                        ? 'linear-gradient(270deg, #222221, transparent)'
                        : 'linear-gradient(270deg, #FDFDFD, transparent)',
                    '& .MuiSvgIcon-root': {
                        width: '24px',
                        height: '24px',
                    },
                },
                [`& .${tabsClasses.scrollButtons}`]: {
                    '&.Mui-disabled': { opacity: 0 },
                },
        }}
        value={value}
        variant="scrollable"
        onChange={handleTabChange}
      >
        {MOCK_TAB_VALUES.map((tab, index) => (
          <Tab
            key={index}
            sx={{ flex:1 }}
            label={( 
              <Box
                sx={{
                    display: 'flex',
                    flexDirection: 'column',
                }}
              >
                <Typography
                  variant={isLargeScreen ? 'headings.h4' : 'buttons.small'}
                  color="inherit"
                >
                  {`${tab.month.slice(0, 3)} ${tab.month.slice(-4)}`}
                </Typography>
                <Typography variant="captions.default" color="inherit">
                  {tab.date}
                </Typography>
              </Box>
            )}
          />
      ))}
      </Tabs>
  );
};

问题图示

Tab未居中显示

解决方案

通过动态判断Tab数量,调整Tabs容器和Tab组件的样式,实现两种布局的适配:

修改后的完整代码

import type { ReactElement } from 'react';
import { useState } from 'react';

import { Box, Tab, Typography, useMediaQuery, useTheme } from '@mui/material';
import Tabs, { tabsClasses } from '@mui/material/Tabs';

const MOCK_TAB_VALUES = [
    { month: 'August 2023', date: '20.08.2023' },
    // { month: 'September 2023', date: '15.09.2023' },
    // { month: 'October 2023', date: '20.10.2023' },
    // { month: 'November 2023', date: '15.11.2023' },
    // { month: 'December 2023', date: '15.11.2023' },
];

export const FormTabComponent = (): ReactElement => {
    const [value, setValue] = useState<number>(0);
    const theme = useTheme();
    const isLargeScreen = useMediaQuery(theme.breakpoints.up('lg'));
    // 获取Tab数量
    const tabCount = MOCK_TAB_VALUES.length;

    const handleTabChange = (event, newValue) => {
        setValue(newValue);
    };
    const isDarkMode = theme.palette.mode === 'dark';
    return (
      <Tabs
        sx={{
                width: 1,
                position: 'relative',
                height: '60px',
                // 根据Tab数量动态设置容器布局
                justifyContent: tabCount === 1 ? 'center' : 'space-evenly',
                // 覆盖内部flex容器样式,解决scrollable变体的默认限制
                [`& .${tabsClasses.flexContainer}`]: {
                    width: '100%',
                    justifyContent: tabCount === 1 ? 'center' : 'space-evenly',
                },
                [`& .${tabsClasses.scrollButtons}`]: {
                    '&.Mui-disabled': { opacity: 0, width: 0 },
                },
                [`& .MuiTabScrollButton-root`]: {
                    opacity: 1,
                },
                '& .MuiTabScrollButton-root:first-child': {
                    zIndex: 1,
                    position: 'absolute',
                    background: isDarkMode
                        ? 'linear-gradient(90deg, #222221, transparent)'
                        : 'linear-gradient(90deg, #FDFDFD, transparent)',
                    '& .MuiSvgIcon-root': {
                        width: '24px',
                        height: '24px',
                    },
                },
                '& .MuiTabScrollButton-root:last-child': {
                    position: 'absolute',
                    right: 0,
                    background: isDarkMode
                        ? 'linear-gradient(270deg, #222221, transparent)'
                        : 'linear-gradient(270deg, #FDFDFD, transparent)',
                    '& .MuiSvgIcon-root': {
                        width: '24px',
                        height: '24px',
                    },
                },
                [`& .${tabsClasses.scrollButtons}`]: {
                    '&.Mui-disabled': { opacity: 0 },
                },
        }}
        value={value}
        variant="scrollable"
        onChange={handleTabChange}
      >
        {MOCK_TAB_VALUES.map((tab, index) => (
          <Tab
            key={index}
            // 根据Tab数量动态设置Tab的flex属性
            sx={{ flex: tabCount > 1 ? 1 : 'auto' }}
            label={( 
              <Box
                sx={{
                    display: 'flex',
                    flexDirection: 'column',
                    alignItems: 'center'
                }}
              >
                <Typography
                  variant={isLargeScreen ? 'headings.h4' : 'buttons.small'}
                  color="inherit"
                >
                  {`${tab.month.slice(0, 3)} ${tab.month.slice(-4)}`}
                </Typography>
                <Typography variant="captions.default" color="inherit">
                  {tab.date}
                </Typography>
              </Box>
            )}
          />
      ))}
      </Tabs>
  );
};

关键修改点

  1. 动态判断Tab数量:新增tabCount变量获取Tab数组长度,作为布局判断依据
  2. 容器布局自适应:
    • 单个Tab时,设置justifyContent: 'center'让容器内容水平居中
    • 多个Tab时,保持space-evenly实现宽度均分
  3. 覆盖内部容器样式:修改.MuiTabs-flexContainer的宽度和布局方式,解决scrollable变体的默认布局限制
  4. Tab组件宽度控制:单个Tab时取消flex:1,让Tab宽度自适应内容;多个Tab时保持flex:1实现均分
  5. Tab内容居中:给Tab内部的Box添加alignItems: 'center',确保文本在Tab内部也居中对齐

内容的提问来源于stack exchange,提问作者mariadev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 02:47:36