Adaptive Layout 是一个 React Native 响应式布局技能,目标是让每个屏幕都针对“当前窗口”而非“设备类型”进行布局,同时覆盖 iOS 与 Android。
用途 它用于把屏幕做成响应式的,并适配平板、iPad、可折叠设备以及多窗口场景。核心思路是:布局决策只依据窗口的尺寸与形状,不依据设备型号或设备方向。
主要能力
- 统一的窗口分类器:src/hooks/use-window-class.ts 提供 { width: 'compact' | 'regular'(600), isLandscape, isLargeText(1.25)},禁止在别处再建第二套分类器。
- 断点与内容规则:超出窗口类别的断点集中在 src/ui/adaptive/pane-layout.ts,以纯函数形式表达内容规则(例如“列表 320 与详情 400 都放得下时显示两栏”),并在窗口表上测试。
- 可读宽度:每个屏幕通过 ScreenFrame、ListDetailLayout 或列表的 contentContainerStyle 将阅读宽度限制在 CONTENT_MAX_WIDTH;画布、地图、相机、视频等全出血屏幕需在 contracts/adaptive-layout.json 的 fullBleedScreens 中登记并说明原因。
- 列表-详情布局:ListDetailLayout 让列表与详情共用一个屏幕,栏数由 useListDetailLayout() 根据自身测量盒决定;紧凑窗口一栏,常规窗口仅在列表、细分隔线与详情最小宽度都放得下时才两栏。
- 网格列数:useMeasuredWidth() + gridColumnsOf({ width, minCellWidth, gap, maxColumns }) 从网格自身容器计算列数,并用 key={
grid-${columns}} 在列数变化时重挂载列表。 - 路由承载选中状态:列表路由带 ?selected=<id>,详情路由为 [id],两路由渲染同一屏幕且各自只读自己的参数;单栏时用 router.push,双栏时用 router.setParams 设置当前路由自己的参数。
- 方向与缩放:应用配置保持 orientation: 'portrait' 或 'default',设置 ios.supportsTablet: true,移除所有退出缩放的配置;每屏方向锁定需在 orientationLocks 中登记原因,且每个 lockAsync 都要有对应的 unlockAsync。
- 安全区:内边距按物理方向处理,旋转会移动刘海或挖孔,需取两侧(或 SafeAreaView edges),不能把刘海侧算作栏宽。
- 检查脚本:scripts/check-adaptive-layout.mjs 对上述规则做零依赖检查,运行后需打印 RESULT: PASS。
使用方式
- 阅读 references/window-classes-and-panes.md,了解窗口与设备的区别、分类器、窗口表、栏规则、可读宽度、网格、固定宽度、大字号等。
- 首次接入应用时,确认 src/hooks/use-window-class.ts 存在,将 templates/src/ui/adaptive/ 连同测试复制到 src/ui/adaptive/,并将 templates/contracts/adaptive-layout.json 及其 schema 复制到 contracts/。
- 配置应用:保持 orientation 为 'portrait' 或 'default',设置 ios.supportsTablet: true,移除所有缩放退出项,并在契约中记录有意选择的配置。
- 屏幕放入 ScreenFrame(或限制列表 contentContainerStyle);全出血屏幕登记到 fullBleedScreens。
- 列表-详情参照 examples/tasks-list-detail.md,网格参照 examples/task-board.tsx。
- 先写测试:窗口表在 fontScale 1 与 2 下测试,使用 useWindowDimensions mock、带 initialMetrics 的 SafeAreaProvider、触发布局事件等。
- 运行 npm run typecheck、npx eslint <changed paths> --max-warnings 0、Jest 测试,再运行 node ${CLAUDE_SKILL_DIR}/scripts/check-adaptive-layout.mjs .,修复所有 FAIL 行直到打印 RESULT: PASS。
- 发布前在命名模拟器与仿真器上按窗口表截图,包括半宽 iPad Split View、fontScale 2 与 RTL 语言环境,并在选中任务时折叠与展开。
适用场景
- 需要同时适配手机、平板、iPad 多窗口(Split View、Slide Over、Stage Manager)、Android 多窗口与自由窗口、可折叠设备的 React Native 应用。
- 需要列表-详情双栏、可读宽度限制、自适应网格列数的界面。
- 需要由路由承载选中状态,使旋转、缩放、恢复路由与链接都落在同一项上。
限制
- 该技能面向 React Native 项目;检查脚本在 package.json 中没有 react-native 时会打印 NOT APPLICABLE。
- 布局规则只读取 UI 代码;src/services/ 中的硬件能力判断不属于布局决策。
- 禁止使用 Platform.isPad、interfaceIdiom、expo-device DeviceType、isTablet()、isPhone、getOrientationAsync 或方向监听器来决定布局。
- 禁止在屏幕内写 width >= 768 之类的原始断点,禁止固定宽度超过 288。
- 禁止在模块顶层使用 Dimensions.get('window')。
- 禁止用 useState 保存选中项,禁止在详情路由调用 router.setParams({ selected })。
- 禁止 numColumns 变化时不加 key。
- 禁止 requireFullScreen: true、resizeableActivity、宽高比限制等退出缩放的配置。
- 具体收费、价格、平台支持细节与效果数据暂未提供。