如何用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数量,调整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> ); };
关键修改点
- 动态判断Tab数量:新增
tabCount变量获取Tab数组长度,作为布局判断依据 - 容器布局自适应:
- 单个Tab时,设置
justifyContent: 'center'让容器内容水平居中 - 多个Tab时,保持
space-evenly实现宽度均分
- 单个Tab时,设置
- 覆盖内部容器样式:修改
.MuiTabs-flexContainer的宽度和布局方式,解决scrollable变体的默认布局限制 - Tab组件宽度控制:单个Tab时取消
flex:1,让Tab宽度自适应内容;多个Tab时保持flex:1实现均分 - Tab内容居中:给Tab内部的Box添加
alignItems: 'center',确保文本在Tab内部也居中对齐
内容的提问来源于stack exchange,提问作者mariadev
相关产品推荐
相关产品推荐

