如何将MUI 5 Switch自定义为双标签切换开关?
MUI 5 Switch 双标签自定义问题解决
需求说明
- 开关总长140px,“A”“B”两侧各占70px,标签始终可见;
- 选中“A”时,“A”侧为主题主色背景+白色文字,“B”侧为白色背景+黑色文字,切换后互换配色;
- 完全隐藏开关的thumb(包括hover和点击状态)。
当前实现代码
import { styled } from "@mui/material/styles"; import Switch, { SwitchProps } from "@mui/material/Switch"; const App = styled(Switch)<SwitchProps>(({ theme }) => ({ width: "140px", height: "42px", padding: 0, border: "1px solid #C6CBD0", borderRadius: "9px", "& .MuiSwitch-switchBase": { transition: theme.transitions.create(["transform", "background-color"], { duration: theme.transitions.duration.shortest, }), "&.Mui-checked": { transform: "translateX(70px)", }, "&.Mui-checked + .MuiSwitch-track": { backgroundColor: theme.palette.secondary.main, opacity: 1, display: "flex", alignItems: "center", justifyContent: "flex-end", "&:before": { content: '"B"', display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 700, fontSize: "20px", width: "50%", color: theme.palette.getContrastText(theme.palette.secondary.main), }, }, "&.Mui-checked + .MuiSwitch-track.Mui-disabled": { backgroundColor: theme.palette.grey[400], }, }, "& .MuiSwitch-thumb": { width: "70px", height: "42px", opacity: 0, }, "& .MuiSwitch-track": { backgroundColor: theme.palette.primary.main, opacity: 1, display: "flex", alignItems: "center", justifyContent: "flex-start", "&:before": { content: "'A'", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 700, fontSize: "20px", width: "50%", color: theme.palette.getContrastText(theme.palette.primary.main), }, }, "&.Mui-disabled": { "& .MuiSwitch-switchBase": { backgroundColor: theme.palette.grey[400], }, }, })); export default App;
修正方案与代码
核心调整点
- 双标签持久显示:给
MuiSwitch-track添加:before和:after两个伪元素,分别固定显示“A”和“B”,各自占据50%宽度; - 背景色切换逻辑:让
MuiSwitch-switchBase作为可滑动的背景块,选中时通过平移覆盖对应侧,track保持白色背景,通过伪元素颜色区分选中状态; - 彻底隐藏thumb:给thumb添加
pointerEvents: 'none',同时设置switchBase的尺寸与单侧一致,避免交互干扰。
修正后的代码
import { styled } from "@mui/material/styles"; import Switch, { SwitchProps } from "@mui/material/Switch"; const CustomToggleSwitch = styled(Switch)<SwitchProps>(({ theme }) => ({ width: 140, height: 42, padding: 0, border: "1px solid #C6CBD0", borderRadius: 9, position: "relative", "& .MuiSwitch-switchBase": { width: 70, height: 40, margin: 0, transition: theme.transitions.create("transform", { duration: theme.transitions.duration.shortest, }), backgroundColor: theme.palette.primary.main, borderRadius: 8, "&.Mui-checked": { transform: "translateX(70px)", backgroundColor: theme.palette.secondary.main, }, "&.Mui-disabled": { backgroundColor: theme.palette.grey[400], }, }, "& .MuiSwitch-thumb": { width: 0, height: 0, opacity: 0, pointerEvents: "none", }, "& .MuiSwitch-track": { width: "100%", height: "100%", backgroundColor: "#fff", opacity: 1, display: "flex", alignItems: "center", justifyContent: "space-around", position: "relative", "&:before, &:after": { content: '""', position: "absolute", width: "50%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 700, fontSize: 20, zIndex: 1, }, "&:before": { content: '"A"', left: 0, color: ({ ownerState }) => ownerState.checked ? "#000" : theme.palette.getContrastText(theme.palette.primary.main), }, "&:after": { content: '"B"', right: 0, color: ({ ownerState }) => ownerState.checked ? theme.palette.getContrastText(theme.palette.secondary.main) : "#000", }, }, "&.Mui-disabled": { opacity: 0.6, borderColor: theme.palette.grey[300], "& .MuiSwitch-track:before, & .MuiSwitch-track:after": { color: theme.palette.grey[500], }, }, })); export default CustomToggleSwitch;
效果说明
- 开关总宽度140px,两侧各70px,“A”“B”标签始终可见;
- 未选中时,“A”侧为主题主色背景+白色文字,“B”侧白色背景+黑色文字;
- 选中后,“B”侧变为主题次色背景+白色文字,“A”侧白色背景+黑色文字;
- thumb完全隐藏,无任何hover/点击时的交互痕迹。
内容的提问来源于stack exchange,提问作者Tigran Petrosyan
相关产品推荐
相关产品推荐

