本节摘要:同一份代码要跑在不同尺寸、不同密度的设备上,靠的是响应式布局。RN 用逻辑像素、百分比、Flexbox 与 Dimensions,Flutter 用逻辑像素、弹性 Widget 与媒体查询。本节讲清尺寸单位的原理、响应式布局的核心手段,以及"什么时候用弹性布局、什么时候按平台写差异"的判断方法。核心准则是"弹性优先、固定例外"。
阅读完本节,你应当能够:
想象同一个 App:在 iPhone SE 上显示刚好,换到大屏平板可能稀疏得可笑;在 2 倍屏上清晰的文字,在 3 倍屏上可能糊成一片。如果开发时把所有尺寸写死,这个 App 只会在"正好那台设备"上好看。
这就是响应式布局要解决的问题:让界面根据设备环境(尺寸、密度、方向)自动调整。核心手段总结起来就两条——用相对尺寸代替固定尺寸,用弹性布局代替绝对定位。理解这两条,适配就不再是玄学。
一个反直觉的点先放在这里:"适配"不等于"每个平台写两套代码"。绝大多数适配问题能用弹性布局解决,真正需要平台专属代码的场景是少数(比如键盘处理、安全区)。过度适配与适配不足都是问题,本节会给你一个判断的标尺。
再把话说透一点:响应式布局的本质是"把界面设计成对空间变化不敏感"。一个弹性良好的界面,塞进任何尺寸的屏幕,结构都成立——只是元素变大变小、行数变多变少。设计阶段就带着这个意识,比事后补适配高效得多。这也是为什么建议你先学透弹性布局,再碰媒体查询:前者是基本功,后者是补充手段。
RN 与 Flutter 都采用逻辑像素作为尺寸单位。逻辑像素是抽象单位,系统按设备像素密度(DPR)把它换算成物理像素。同样是 100 宽,在 2 倍屏上占 200 物理像素,在 3 倍屏上占 300 物理像素——但视觉大小一致。
这个机制的好处是:你写一套尺寸,框架自动适配各种密度屏幕,无需手写"如果 3 倍屏就加大字号"这类分支。这也是为什么 RN 里 width: 200 不加单位、Flutter 里宽高都是数字——它们默认就是逻辑像素。
弹性布局是响应式的第一主力。RN 的 Flexbox 与 Flutter 的 Expanded、Flexible 都是弹性手段:让元素按比例分配空间,而非占用固定尺寸。
// RN:三个子元素等分宽度 <View style={{ flexDirection: 'row' }}> <View style={{ flex: 1, backgroundColor: 'red' }} /> <View style={{ flex: 1, backgroundColor: 'green' }} /> <View style={{ flex: 1, backgroundColor: 'blue' }} /> </View>
// Flutter:三个子元素等分宽度 Row( children: [ Expanded(child: Container(color: Colors.red)), Expanded(child: Container(color: Colors.green)), Expanded(child: Container(color: Colors.blue)), ], )
两种写法思路一致:flex: 1 或 Expanded 让元素"弹性占位"。屏宽变宽,三者同步变宽,比例不变。这就是"会伸缩"的含义。
弹性布局之外,百分比是常用的相对尺寸。RN 直接支持百分比作为样式值:width: '50%' 表示父容器宽度的一半,子元素可以层层相对。Flutter 没有直接的百分比语法,用 FractionallySizedBox 实现按父容器比例分配尺寸。
// Flutter 用 FractionallySizedBox 实现占父容器一半宽度 FractionallySizedBox( widthFactor: 0.5, child: Container(color: Colors.blue), )
两种框架的百分比能力殊途同归,用途一致:当某个元素需要"始终占父容器的某个比例"时使用。选择弹性还是百分比,看需求更接近"按比例瓜分空间"(弹性)还是"占父容器固定比例"(百分比)。
弹性布局解决的是"尺寸变化",但有些适配需要"环境感知":
RN 的 Dimensions。通过 Dimensions.get('window') 获取屏幕宽高,动态计算尺寸。适合"根据屏幕宽度计算网格列数"这类需求。
Flutter 的 MediaQuery。提供屏幕尺寸、方向、文字缩放等信息,配合布局 Builder 在 build 里读取并调整布局。
平台判断。RN 的 Platform.OS 与 Flutter 的 Platform 类都用于区分平台写差异代码。判断标准是:这个差异是"平台特性"还是"内容差异"。平台特性(系统返回键行为、安全区、键盘处理)需要平台判断;内容差异(只是尺寸不同)应该交给弹性布局,别用平台判断硬写。
Flutter 里还有一个进阶场景值得提一句:根据屏幕宽度切换整个布局结构,而不是只调尺寸。比如宽屏显示两栏(列表加详情),窄屏只显示单栏。这类需求用 MediaQuery 判断宽度,再条件渲染不同 Widget 树即可。这也是"响应式"从"调尺寸"进化到"换结构"的分水岭——真正复杂的响应式,改变的不只是大小,还有信息组织方式。这个思路在第 5、6 章的实战里会用到,先建立概念。

这张图把适配手段分成三层:逻辑像素打底、弹性布局为主、环境感知补充。按这个层级思考适配,很少会走偏。
| 需求 | React Native | Flutter |
|---|---|---|
| 尺寸单位 | 逻辑像素(默认) | 逻辑像素(默认) |
| 百分比 | 支持 | 通过 FractionallySizedBox |
| 弹性分配 | flex 属性 | Expanded / Flexible |
| 屏幕尺寸 | Dimensions API | MediaQuery |
| 平台判断 | Platform.OS | Platform 类 |
写界面时先问自己两个问题,能避免九成适配错误:
问题一:这个尺寸是相对父容器还是绝对的? 相对就用弹性或百分比,绝对才用固定值。
问题二:这个差异是平台特性还是内容差异? 平台特性(安全区、返回键、键盘)写平台分支;内容差异(字体大小、间距)交给弹性布局。
⚠️ 常见坑:拿真机尺寸做开发基准。在开发机上调好的固定尺寸,换一台不同比例的机器就崩。始终用相对与弹性布局作为默认,固定尺寸只用于确实需要固定的元素(图标尺寸、分割线宽度)。
💡 关键直觉:适配的黄金准则是"弹性优先、固定例外"。把固定尺寸当成"例外"来用,而不是默认,适配问题会少掉一大半。
回到 4.3 的待办界面,做三个适配练习:输入框与按钮并排时用 flex 分配宽度(按钮固定、输入框弹性);列表项在横屏时自动排成两列(用 Dimensions 或 MediaQuery 判断宽度);在窄屏手机上验证没有溢出。做完这三个练习,你会直观感受到"弹性优先"带来的效果。
问:逻辑像素和真实像素到底是什么关系? 逻辑像素是抽象单位,系统按设备像素密度(DPR)换算成物理像素。比如逻辑像素 100 宽,在 2 倍屏占 200 物理像素、在 3 倍屏占 300 物理像素,但看起来大小一致。开发者只写逻辑像素,密度换算由框架处理——这就是"一套尺寸处处适用"的原理。
问:什么时候需要真正区分平台写代码? 分两类:平台特性类(iOS 的安全区、Android 的返回键、两端的键盘行为)通常需要平台判断;视觉差异类(只是某个间距、字号不同)尽量用统一布局加弹性方案。一个经验:能用统一代码解决的就别拆平台分支,分支越多,维护成本越高。
问:iPad 和大屏 Android 平板怎么适配? 平板屏幕远大于手机,单靠弹性布局会显得空旷。需要"换结构"式的响应式:宽屏时用多栏布局、放大字体、调整间距。Flutter 用 MediaQuery 判断宽度切换布局树,RN 用 Dimensions 加条件渲染。平板适配是响应式的进阶练习,先把手机端弹性做好,再逐步覆盖平板。
问:横竖屏切换会触发什么? 屏幕尺寸变化会触发布局重新计算。RN 里 Dimensions 变化需要监听,Flutter 里 MediaQuery 在 build 时会读到新尺寸。设计时尽量让布局对横竖屏都健壮,而不是只在竖屏验证过。
响应式布局做没做对,不是看代码,而是看它在各种尺寸下的表现。测试方法不必复杂:模拟器支持切换设备尺寸,把常用的小屏、中屏、大屏机型各跑一遍;真机上重点验证主流机型与刘海屏。关注点集中在三处:是否有内容溢出、是否有元素被截断、间距比例是否失衡。这套"多尺寸过一遍"的验证习惯,比反复改代码猜效果好得多。等到界面在五六个尺寸下都稳定,适配才算真正完成。
适配的对面是"过度适配"——为每一种可能尺寸都写专门的分支代码。过度适配的代码比适配不足更可怕:分支爆炸、维护成本陡增、新尺寸出现时还要继续加。正确的态度是"弹性为主、分支为辅":先让弹性布局解决大部分问题,只有少数真正需要区分场景(平板宽屏 vs 手机竖屏)才写条件分支。记住一个判断:如果某个适配分支不写,界面也只是"略难看"而非"不可用",那它就不该写。
适配不只有布局,字体与间距同样需要弹性。RN 与 Flutter 默认都用逻辑像素,所以字体在正常密度下已经统一;真正需要关注的是"辅助大字体"设置——系统放大字体后,你的界面文字是否还能完整显示、布局是否还成立。处理思路与布局一致:别写死高度,让文字可以换行;间距用相对值而非绝对值。测试时把系统字体调到最大跑一遍,能提前发现大量"文字溢出"问题。这条经验在实际项目中比想象中更常被忽略。
组件体系与适配都讲完了,界面能搭、能适配了。下一章进入更深的一层——数据怎么流动,状态怎么管理,让界面真正"活"起来。