@ComposablefunBoxWithConstraints(modifier:Modifier=Modifier,contentAlignment:Alignment=Alignment.TopStart,propagateMinConstraints:Boolean=false,content:@ComposableBoxWithConstraintsScope.()->Unit,){SubcomposeLayout(modifier){constraints->// We're NOW in the measurement phase, but we can compose!valscope=BoxWithConstraintsScopeImpl(this,constraints)valmeasurables=subcompose(Unit){scope.content()}// Standard Box measurement logic follows...with(measurePolicy){measure(measurables,constraints)}}}
interfaceBoxWithConstraintsScope:BoxScope{valminWidth:DpvalmaxWidth:DpvalminHeight:DpvalmaxHeight:Dpvalconstraints:Constraints// The raw pixel-based constraints}
BoxWithConstraints{// These are NOT the screen dimensions!valwidth=maxWidth// Space YOUR PARENT gave YOUvalheight=maxHeight// Space YOUR PARENT gave YOU}
考虑以下层级结构:
1234567
Row(Modifier.fillMaxSize()){NavigationRail{/* 80.dp wide */}BoxWithConstraints(Modifier.weight(1f)){// maxWidth here is (screenWidth - 80.dp)// NOT screenWidth!}}
// ✅ Just use fillMaxSize with a fractionBox(Modifier.fillMaxSize(0.5f))
2. 当你只是检查屏幕方向时
1234
// ❌ Using BoxWithConstraints for screen-level decisionsBoxWithConstraints{if(maxWidth>maxHeight)LandscapeLayout()elsePortraitLayout()}
1234567
// ✅ Use LocalConfiguration for screen-level decisionsvalconfiguration=LocalConfiguration.currentif(configuration.orientation==Configuration.ORIENTATION_LANDSCAPE){LandscapeLayout()}else{PortraitLayout()}
3.在 LazyColumn/LazyRow 元素内部
这是一个常见的错误:
12345678
// ❌ Performance issueLazyColumn{items(1000){index->BoxWithConstraints{// Subcomposition per item!ItemContent(index)}}}
// ✅ Single subcompositionBoxWithConstraints{valitemHeight=if(maxWidth>400.dp)120.dpelse80.dpLazyColumn{items(1000){index->ItemContent(index,height=itemHeight)}}}
更轻量级的替代方案
基于百分比的尺寸:
12
// Use Modifier.fillMaxWidth(fraction)Box(Modifier.fillMaxWidth(0.8f))
12345
// Use Modifier.weight in Row/ColumnRow{Box(Modifier.weight(1f))// 25%Box(Modifier.weight(3f))// 75%}Foraspectratio:
1
Box(Modifier.aspectRatio(16f/9f))
不使用子组件的自定义尺寸逻辑:
1234567
// Custom Layout is lighter than SubcomposeLayoutLayout(content={/* your content */}){measurables,constraints->// Full control over measurement without subcomposition cost// But you can't make composition decisions here}
Row{Text("Hello",modifier=Modifier.height(IntrinsicSize.Min))BoxWithConstraints{// CRASH: "Asking for intrinsic measurements of // SubcomposeLayout layouts is not supported"}}
BoxWithConstraints{// This state is created during MEASUREMENT, not compositionvarcountbyremember{mutableStateOf(0)}// If parent re-measures without recomposing, this state persists// But if the slot is "recycled", it might reset}
最佳实践:将重要状态提升到 BoxWithConstraints 之上:
1
varcountbyremember{mutableStateOf(0)}
1234
BoxWithConstraints{// count is stable regardless of subcomposition lifecycleText("Count: $count")}
5. propagateMinConstraints 参数
12345
BoxWithConstraints(propagateMinConstraints=true){// Children now MUST be at least minWidth x minHeight// This can cause unexpected overflow if children // have their own size requirements}