Flutter 像素级还原设计稿到代码的视觉无损转换技巧一、1px 的偏差用户看得出来做过 UI 还原的开发者都有这种经历对着设计稿调了一下午感觉差不多了结果设计师走过来扫一眼就说这里间距是 12px 不是 14px、这个阴影扩散半径不对、文字行高跟设计稿不一样。这种对话在 Flutter 开发中尤其常见因为 Flutter 的渲染机制和 Web 完全不同。Web 里的 1px 就是 1 个 CSS 像素但 Flutter 里的逻辑像素logical pixel和设备像素device pixel之间有devicePixelRatio的转换。再加上 Flutter 自带的 Material 主题会隐式覆盖你的样式——不搞清楚这些机制你永远在做差不多的还原。这篇文章我会分享一套我用了两年的 Flutter 像素级还原工作流——从 Figma 参数提取到 Flutter 代码的精确映射覆盖间距、圆角、阴影、字体、渐变五个最容易出偏差的维度。二、Figma 参数到 Flutter 参数的精确映射表间距Figma 的 px 和 Flutter 的 px 等价吗答案在逻辑像素层面等价但前提是你设置对了。Figma 默认使用 1x 设计尺寸通常 375 或 390 宽度的 iPhone 设计稿。Flutter 的逻辑像素在 iPhone 14 Pro390×844 logical pixels上天然就是 1:1 对应。但这里有一个陷阱// 错误在 MaterialApp 里开启 useMaterial3 可能导致默认有 padding MaterialApp( useMaterial3: true, // M3 的某些控件有隐式内边距 home: MyPage(), ) // 正确显式关闭不需要的默认样式 MaterialApp( useMaterial3: true, theme: ThemeData( useMaterial3: true, // 重置所有默认内边距 visualDensity: VisualDensity.standard, // 关键关闭 AppBar 的默认 elevation appBarTheme: const AppBarTheme( scrolledUnderElevation: 0, elevation: 0, ), ), )阴影Figma 的 4 个参数如何映射到 FlutterFigma 的阴影参数是X 偏移、Y 偏移、模糊Blur、扩散Spread、颜色透明度。Flutter 的BoxShadow接受offset对应 X/Y、blurRadius对应 Blur、spreadRadius对应 Spread、color。95% 的情况直接映射即可// Figma: shadow X0, Y4, Blur16, Spread0, Color#000 10% BoxShadow( offset: const Offset(0, 4), blurRadius: 16, spreadRadius: 0, color: Colors.black.withOpacity(0.10), )但有一个卡住很多人的细节Figma 的阴影色是用rgba(0,0,0,0.1)表示的而 Flutter 的withOpacity会乘以当前 color 的 alpha。所以以下两种写法是不同的// 写法 AColors.black.withOpacity(0.1) → alpha 0.1 ✓ Colors.black.withOpacity(0.1) // 写法 BColor(0x1A000000) → alpha 0x1A/0xFF ≈ 0.102 ✓ Color(0x1A000000) // 写法 CColors.black.withOpacity(0.5).withOpacity(0.1) → alpha 0.05 ✗ 错误规则永远只调用一次 withOpacity或者在构造函数中直接指定 alpha 通道。三、五个最容易出偏差的细节及解决方案1. 字体行高Line Height的 Flutter 换算Figma 的行高有两种模式固定值如20px和百分比如150%。Flutter 的TextStyle.height是一个乘数multiplier不是固定值// Figma: fontSize14, lineHeight20 (固定) // Flutter 换算: height 20/14 ≈ 1.4286 TextStyle( fontSize: 14, height: 20 / 14, // 1.4286 ) // Figma: fontSize16, lineHeight150% (百分比) // Flutter 换算: height 1.5 TextStyle( fontSize: 16, height: 1.5, )关键细节Flutter 的height是相对于fontSize的但它的计算基准是字体的 em-square不是实际字形高度。中文和英文混排时行高会有视觉偏差。我的做法是先算理论值然后在设备上对比截图通常需要 ±0.05 的微调。2. 渐变角度的坐标系差异Figma 的渐变角度0° 从左到右顺时针旋转。Flutter 的LinearGradient.begin和end基于Alignment坐标系Alignment(-1, -1)是左上角。// Figma: 渐变角度 135°从左上到右下 // Flutter 换算 LinearGradient( begin: Alignment.topLeft, // 对应 135° 的起点 end: Alignment.bottomRight, // 对应 135° 的终点 colors: [Color(0xFF667EEA), Color(0xFF764BA2)], ) // 如果需要精确的任意角度 import dart:math; Alignment angleToAlignment(double degrees) { final rad (degrees - 90) * pi / 180; // 减 90° 因为 Flutter 坐标系差异 final dx cos(rad); final dy sin(rad); return Alignment(dx, dy); } // Figma: 角度 73° LinearGradient( begin: angleToAlignment(73 180), // 终点是对面 end: angleToAlignment(73), colors: [...], )3. MediaQuery 的 padding 陷阱// 错误直接使用 MediaQuery.of(context).size 做布局计算 // 这个 size 包含了状态栏、底部安全区等系统 UI final screenHeight MediaQuery.of(context).size.height; // 正确使用 padding 减去系统 UI 区域 final padding MediaQuery.of(context).padding; final safeHeight MediaQuery.of(context).size.height - padding.top - padding.bottom;4. 圆角的连续曲线 vs 标准曲线Figma 支持连续曲线圆角即 corner smoothingiOS 的超椭圆圆角。Flutter 不直接支持但可以用ContinuousRectangleBorder近似// Figma 的圆角平滑度Corner Smoothing设为 60% // 使用 ContinuousRectangleBorder 作为近似 ClipRRect( borderRadius: BorderRadius.circular(16), // 或使用 clipper: ShapeBorderClipper( shape: ContinuousRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), child: ..., )5. 图片的缩放模式与 Figma 的 Fill/Fit 对应Figma 填充模式Flutter BoxFit说明FillBoxFit.fill拉伸填满会变形FitBoxFit.contain完整显示可能有留白CropBoxFit.cover最常用裁剪填满NoneBoxFit.none原始尺寸四、我的像素级还原检查清单每次做完 UI 还原我会对照这份清单逐项自查间距检查在 Flutter Inspector 中开启Show Layout Guidelines逐一对照 Figma 的间距标注。特别注意ListView的paddingvsitemExtent导致的额外间距。阴影对比截屏后和 Figma 导出图叠图对比50% 透明度重点关注阴影的扩散范围和颜色深度。字体检查使用flutter_screenutil或直接利用MediaQuery.textScaleFactor验证字体缩放后的表现。圆角一致性卡片、按钮、输入框的圆角值是否与设计系统一致——同一层级的所有容器圆角应该相同。安全区验证在 iPhone X 及以上机型的模拟器中检查底部安全区和灵动岛的适配。暗黑模式切换到暗黑模式后所有颜色是否正确映射参考上一篇文章的暗黑模式适配算法。五、总结像素级还原的本质不是看得像就行而是参数一一对应。Figma 的每个设计参数间距、圆角、阴影参数、字体配置、渐变角度都有它在 Flutter 中的精确映射方式。知道这些映射规则你能在半小时内完成以前需要两天的 UI 还原工作。更重要的——当设计师再来说这里差 2px的时候你有底气告诉 TA到底是谁的稿子标错了。作者李慕杰Leo / 8limujie一个被设计师追着改了三年间距、终于写完这套映射表的前端匠人