Appearance
Lab: Flutter asset 变体(1x 图在 3x 屏的放大发虚与变体匹配)
对应知识库文档:逻辑单位、资源倍率与图片解码:dp / dpi / scale 到内存(§底层机制 2 密度桶解码缩放 / §5 跨端对照 / §5.1 Flutter AssetImage 变体尺寸计算流程) 上级章节:屏幕适配:从清晰显示到动态窗口可用
这个 lab 验证什么
在真实 Flutter 工程里验证文档讲的「按 devicePixelRatio 找 1.0x/2.0x/3.0x 变体、缺失时不额外解码重采样」:
- 内存不预放大解码:只有 1x 的图,不管设备 dpr 是 1.0 还是 4.0,解码像素恒为原像素(48×48)、
key.scale恒为 1.0——Flutter 不在内存里按 density 预重采样放大(对比 Android 低桶会被解码放大 ×9)。 - 逻辑尺寸与发虚(上屏) :自然尺寸渲染时固有逻辑尺寸
,在 3x 屏上占据完整的 48dp 显示空间(绝对不缩小);但由于 48px 无法填满 3x 屏要求的 144px 物理点阵,48px 源被 GPU 放大拉满,肉眼可见偏软 / 模糊 / 锯齿。 - 变体选择规则:
_chooseAsset返回第一张 scale ≥ dpr 的变体,若一张都没有则回退最高档。滑块模拟 dpr 可实时观察:1.0→1x,1.5/2.0→2x,2.625/3.0→3x,4.0→回退 3x。
运行方式
bash
cd labs/flutter/host
flutter pub get
flutter run # 选一个模拟器/真机,进入首页后点「图片资源变体 Demo」页面内容
素材是程序生成的「网格 + 圆 + 角标」PNG(线宽随分辨率等比:1px@48 / 2px@96 / 3px@144),角标色 = 命中的变体(1x 蓝 / 2x 绿 / 3x 橙),肉眼即可分辨命中了哪一档:
| 素材 | 像素 | 说明 |
|---|---|---|
assets/asset_variant_demo/logo_1x_only.png | 48×48 | 无任何变体,永远按 1.0x 解析 (scale=1.0) |
assets/asset_variant_demo/logo.png | 48×48 | 1.0x(带 2.0x / 3.0x 变体) |
assets/asset_variant_demo/2.0x/logo.png | 96×96 | 2.0x 变体 |
assets/asset_variant_demo/3.0x/logo.png | 144×144 | 3.0x 变体 |
页面内:
- 真实设备信息:真实
devicePixelRatio、素材规格、期望逻辑尺寸(120dp)。 - 模拟 dpr 滑块(1.0~4.0):驱动下方「解码像素探针」,观察变体选择规则与解码像素。
- 真机表现 A/B/C 三卡:
- A · 只有 1x · 自然尺寸 → 渲染为 48dp 固有逻辑尺寸(无缩小),48px 点阵被 GPU 拉大发虚;
- B · 只有 1x · 强制 120dp → 布局强制 120dp,48px 源被 GPU 放大至 360px(7.5×),严重马赛克 / 锯齿;
- C · 带 1x/2x/3x · 自然尺寸 → 命中 3.0x(144px),
固有尺寸,且 144px 点对点 1:1 绝对清晰。
- 解码像素探针:同一模拟 dpr 下,对比「只有 1x」与「带变体」两份素材的解码结果。
预期现象
- 只有 1x 的素材:把滑块从 1.0 拖到 4.0,解码像素恒为 48×48、scale 恒为 1.0——「缺失不缩放」。
- 带变体的素材:dpr=1.0 → 48×48(1.0x);dpr=2.0 → 96×96(2.0x);dpr=2.625 / 3.0 → 144×144(3.0x);dpr=4.0 → 仍 144×144(无 ≥4 的变体,回退最高档)。
- 真机三卡:A 尺寸正常(48dp)但发虚偏软、B 糊、C 清晰;C 若也强制 120dp,同样会糊(变体只保证「设计逻辑尺寸」下的锐度,救不了超出设计尺寸的放大)。
代码入口
- asset_variant_demo_page.dart —— 演示页面(
routeName = '/asset_variant_demo',_ImageInfoLine用AssetImage.resolve(ImageConfiguration(devicePixelRatio: dpr))实时打印解码结果)。 - 路由注册:app_router.dart(import + route const + switch case + 首页列表项)。
- 素材声明:pubspec.yaml 的
assets:段(4 个文件,含 2.0x / 3.0x 变体目录)。 - 素材生成脚本:generate_assets.py(纯 Python 手写 PNG,网格 + 圆 + 角标色;
python3 generate_assets.py可重新生成)。