用 Compose 写自适应布局,你大概率踩过这些坑:
Row 放不下就溢出,FlowRow 能换行但子项大小不好控。想让某几个子项按比例分配剩余空间?对不起,手动算 weight 和 Modifier.width 去吧。
Google 终于把 CSS 世界里最成熟的弹性布局方案搬进了 Compose ------ FlexBox。

FlexBox 是什么
FlexBox 是 androidx.compose.foundation.layout 包下新增的容器组件,目前处于 Experimental 阶段(需要 @ExperimentalFlexBoxApi 注解)。
它的设计几乎 1:1 对齐 CSS Flexible Box Layout 规范。如果你写过前端的 display: flex,上手成本约等于零。
核心能力三句话说完:
-
• 子项可伸缩 :通过
grow/shrink控制子项如何分配多余空间或吸收不足空间 -
• 自动换行:空间不够时子项自动折到下一行/列
-
• 多维对齐:主轴分布、交叉轴对齐、多行间距,一套 API 全搞定

和现有布局的差别
Compose 已经有 Row、Column、FlowRow、FlowColumn,为什么还需要 FlexBox?
Row / Column :单行/单列布局。Row 放不下就直接溢出裁剪,没有换行能力。weight 修饰符可以按比例分配空间,但不支持 shrink 语义------空间不足时子项不会主动缩小,只会被裁剪。
FlowRow / FlowColumn:支持换行,但子项大小控制有限。你不能指定"这个子项优先占 200dp,如果有剩余空间再按比例扩展"这种行为。它更像一个"能换行的 Row",而不是弹性布局。
FlexBox:换行 + 伸缩 + 对齐,三件事一起解决。
具体差异:
| 能力 | Row/Column | FlowRow/FlowColumn | FlexBox | | --- | --- | --- | --- | | 换行 | 不支持 | 支持 | 支持 | | 子项伸展 (grow) | weight | 有限支持 | 原生支持 | | 子项收缩 (shrink) | 不支持 | 不支持 | 原生支持 | | 初始尺寸 (basis) | 不支持 | 不支持 | 支持 dp / 百分比 / Auto | | 子项排序 (order) | 不支持 | 不支持 | 支持 | | 多行对齐 (alignContent) | 不适用 | 有限 | 完整支持 |
一句话总结:如果你的布局需要"换行 + 伸缩",FlexBox 是目前唯一的原生方案。FlowRow 做不了弹性伸缩,Row 做不了换行。

怎么接入
FlexBox 在 compose foundation-layout 1.11.0-beta02 及以上版本可用。
lib.versions.toml 加版本:
bash
[versions]
compose = "1.11.0-beta02"
[libraries]
androidx-compose-foundation-layout = {
group = "androidx.compose.foundation",
name = "foundation-layout",
version.ref = "compose"
}
build.gradle.kts 加依赖:
bash
dependencies {
implementation(libs.androidx.compose.foundation.layout)
}
因为是 Experimental API,使用时需要加注解:
bash
@OptIn(ExperimentalFlexBoxApi::class)
使用方法
基础用法
最简单的 FlexBox------两个 Text 居中排列:
bash
FlexBox(
config = {
direction(FlexDirection.Column)
alignItems(FlexAlignItems.Center)
}
) {
Text(text = "Hello", fontSize = 48.sp)
Text(text = "World!", fontSize = 48.sp)
}
config 里设置容器行为,direction 控制排列方向(Row 或 Column),alignItems 控制交叉轴对齐。和 CSS flex 的写法几乎一样。
换行 + 弹性伸展
实际开发中最常用的场景:一组子项排不下就换行,换行后每行的子项自动填满剩余空间。
bash
FlexBox(
config = {
wrap(FlexWrap.Wrap)
gap(8.dp)
}
) {
RedRoundedBox()
BlueRoundedBox()
GreenRoundedBox(modifier = Modifier.flex { grow(1.0f) })
OrangeRoundedBox(modifier = Modifier.flex { grow(1.0f) })
PinkRoundedBox(modifier = Modifier.flex { grow(1.0f) })
}
wrap(FlexWrap.Wrap) 开启换行,gap(8.dp) 设置子项间距(水平和垂直统一)。
Modifier.flex { grow(1.0f) } 表示这个子项参与剩余空间分配。没设置 grow 的子项保持固有尺寸,设置了的子项按 grow 值的比例瓜分剩余空间。

grow 和 shrink:空间分配的核心
grow 控制多余空间怎么分。三个子项各 100dp,容器 600dp,多了 300dp:
bash
FlexBox {
RedBox(Modifier.flex { grow(1f) }) // 100 + (1/6)*300 = 150dp
BlueBox(Modifier.flex { grow(2f) }) // 100 + (2/6)*300 = 200dp
GreenBox(Modifier.flex { grow(3f) }) // 100 + (3/6)*300 = 250dp
}
grow 值 1:2:3,多余的 300dp 就按 50:100:150 分配。
shrink 控制空间不够时谁缩小。默认 shrink 值是 1f(所有子项等比收缩)。把某个子项的 shrink 设为 0f,它就不会缩小:
bash
FlexBox {
Text(
"The quick brown fox",
modifier = Modifier.flex { shrink(1f) } // 会缩小
)
Text(
"The quick brown fox",
modifier = Modifier.flex { shrink(0f) } // 不缩小
)
}
容器变窄时,第一个 Text 会被压缩换行,第二个保持原始宽度不变。
basis:指定初始尺寸
basis 决定子项在分配空间之前的"起始尺寸",三种写法:
bash
// 自动:使用子项固有尺寸
Modifier.flex { basis(FlexBasis.Auto) }
// 固定 dp
Modifier.flex { basis(200.dp) }
// 百分比:占容器的 70%
Modifier.flex { basis(0.7f) }
basis + grow 组合是 FlexBox 最强大的地方。比如你想让两个子项分别占 70% 和 30%,直接用百分比 basis 就行,不需要嵌套 Row 再算 weight。
order:不改代码顺序,改视觉顺序
bash
FlexBox {
RedBox(title = "World") // 声明在前,但显示在后
BlueBox(
title = "Hello",
modifier = Modifier.flex { order(-1) } // order 更小,排在前面
)
}
order 默认值是 0,FlexBox 按 order 升序排列。需要把某个子项移到最前面?给它一个负值就行。
这在响应式布局中很实用------不同屏幕尺寸下,你可能希望同一组子项的展示顺序不同,用 order 比条件判断加重组干净得多。

适用场景和边界
FlexBox 适合用在:
-
• 标签云 / 筛选器:不定数量的 Chip,自动换行,每行撑满
-
• 响应式卡片网格:卡片宽度弹性,窄屏一列、宽屏三列,不需要手动判断
-
• 表单布局:Label + Input 按比例分配,窄屏自动折行
-
• 工具栏:按钮组在宽屏一行展示,窄屏自动换行
FlexBox 不适合:
-
• 大量数据列表 :FlexBox 不支持懒加载,大量子项请用
LazyColumn/LazyVerticalGrid -
• 整体页面骨架 :页面级布局用
Scaffold+Grid更合适,FlexBox 适合局部区域
官方的建议也很明确:FlexBox 用于少量子项的局部布局,不是 LazyLayout 的替代品。
最后
FlexBox 目前是 Experimental,API 可能还会调整。但它填补的是 Compose 布局体系里一个真实的空缺------换行 + 弹性伸缩的组合,之前没有原生方案能干净地实现。
如果你之前在用 FlowRow + 手动计算宽度来模拟弹性布局,现在可以开始迁移了。