如何实现两个垂直堆叠的MUI手风琴占父容器50%且内容独立滚动
解决React MUI手风琴垂直堆叠的固定标题与动态高度需求
核心思路
通过flex布局嵌套结合动态样式控制,实现手风琴的以下行为:
- 单个手风琴展开时占据父容器全部可用空间
- 两个手风琴都展开时各占父容器50%高度
- 手风琴内容溢出时仅内容区滚动,标题(AccordionSummary)保持固定
关键实现步骤
- 父容器布局:设置
display: flex、flex-direction: column和固定高度,为子元素分配空间提供基础 - 手风琴容器动态flex控制:根据其他手风琴的展开状态,动态设置当前手风琴容器的
flex属性,实现空间分配逻辑 - 重写MUI手风琴组件样式:将Accordion改为flex column布局,固定AccordionSummary高度,让AccordionDetails成为可滚动区域
完整代码示例
import { useState } from 'react'; import { Accordion, AccordionSummary, AccordionDetails, Typography, Box } from '@mui/material'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; const StackedAccordions = () => { const [expanded, setExpanded] = useState({ accordion1: false, accordion2: false, }); const handleChange = (panel) => (event, isExpanded) => { setExpanded(prev => ({ ...prev, [panel]: isExpanded })); }; // 根据展开状态计算当前手风琴容器的flex值 const getFlexValue = (panel) => { const { accordion1, accordion2 } = expanded; if (panel === 'accordion1') { return !accordion2 ? (accordion1 ? '1 1 auto' : '0 0 auto') : (accordion1 ? '1 1 50%' : '0 0 auto'); } else { return !accordion1 ? (accordion2 ? '1 1 auto' : '0 0 auto') : (accordion2 ? '1 1 50%' : '0 0 auto'); } }; return ( <Box sx={{ height: '80vh', display: 'flex', flexDirection: 'column', gap: 2, padding: 2, backgroundColor: '#f5f5f5' }}> {/* 第一个手风琴容器 */} <Box sx={{ flex: getFlexValue('accordion1'), display: 'flex', flexDirection: 'column', minHeight: 0 }}> <Accordion expanded={expanded.accordion1} onChange={handleChange('accordion1')} sx={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', }} > <AccordionSummary expandIcon={<ExpandMoreIcon />} sx={{ flex: '0 1 auto' }} > <Typography>手风琴标题 1</Typography> </AccordionSummary> <AccordionDetails sx={{ flex: '1 1 auto', overflowY: 'auto', minHeight: 0, padding: 2, }} > {/* 模拟长内容 */} {Array.from({ length: 50 }).map((_, idx) => ( <Typography key={idx}>内容行 {idx + 1}</Typography> ))} </AccordionDetails> </Accordion> </Box> {/* 第二个手风琴容器 */} <Box sx={{ flex: getFlexValue('accordion2'), display: 'flex', flexDirection: 'column', minHeight: 0 }}> <Accordion expanded={expanded.accordion2} onChange={handleChange('accordion2')} sx={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', }} > <AccordionSummary expandIcon={<ExpandMoreIcon />} sx={{ flex: '0 1 auto' }} > <Typography>手风琴标题 2</Typography> </AccordionSummary> <AccordionDetails sx={{ flex: '1 1 auto', overflowY: 'auto', minHeight: 0, padding: 2, }} > {/* 模拟长内容 */} {Array.from({ length: 50 }).map((_, idx) => ( <Typography key={idx}>内容行 {idx + 1}</Typography> ))} </AccordionDetails> </Accordion> </Box> </Box> ); }; export default StackedAccordions;
关键细节解释
- flex动态分配:
getFlexValue函数根据两个手风琴的展开状态,决定当前容器的空间占比——单个展开时占满剩余空间,两个都展开时各占50% - 手风琴内部布局:将Accordion设置为
flex-direction: column并高度100%,让Summary固定(flex: 0 1 auto),Details占据剩余空间并开启滚动(overflowY: auto) - minHeight: 0:解决flex布局中子元素内容溢出时不触发滚动的问题,这是flex布局的常见特性,必须添加才能让overflow生效
内容的提问来源于stack exchange,提问作者paullc
相关产品推荐
相关产品推荐

