Flutter 启动页白天模式改造:从“手绘耳机”到透明 PNG 资源接入 最近在做一个 AI 耳机 App 的 Welcome 启动页 UI 改造。这个页面原本已经有一版夜间模式而且夜间模式的布局、按钮、耳机位置和交互都已经基本定稿。因此这次改造的目标不是重新设计一个页面而是在不破坏夜间模式的前提下为白天模式补上一套更干净、更高级的浅蓝灰智能硬件风格。最终目标风格大致是浅蓝灰背景 低饱和蓝色点缀 白色半透明质感 柔和阴影 透明白色耳机主视觉 简约高级的智能硬件产品感一、问题背景最开始白天模式效果并不理想主要问题集中在几个地方耳机像是用 Flutter Widget 或 CustomPaint 临时画出来的质感不够。耳机区域出现明显的灰色或白色矩形底板。背景波纹和耳机没有融合看起来像直接贴了一张图。logo 资源异常显示出了多余画布或非目标内容。页面整体只是“夜间模式换浅色皮肤”没有智能硬件欢迎页的空气感。一开始我以为是布局没调好后来排查发现核心问题并不是布局而是资源和实现路线不对。夜间模式可以用代码绘制出比较酷的深色霓虹效果但白天模式想要做出透明、轻拟物、玻璃感的耳机如果继续用 Flutter Widget 强行画很难接近目标效果。二、核心原则这次改造定了几个原则夜间模式保持不变。不改路由、不改登录跳转、不改按钮点击逻辑。白天模式耳机不再用 Flutter 手动画。白天模式耳机必须使用真正透明背景的 PNG。背景和 logo 可以用 Flutter 代码绘制避免依赖异常图片资源。不用 Container、ClipRRect、Opacity、ColorFilter 去“伪装”素材问题。也就是说耳机必须是干净的图片资源背景和 logo 则可以通过轻量 CustomPaint 实现。三、素材检查白天模式耳机素材统一放在assets/images/transparent_earphones.png重新检查后的图片信息如下size: 1254 x 1254 mode: RGBA has_alpha: true transparent pixels: about 71.8% corners alpha: 0, 0, 0, 0这说明图片确实有 alpha 透明通道四个角也是透明的不会天然显示白色或灰色矩形底板。pubspec.yaml中使用目录方式引入资源flutter: uses-material-design: true assets: - assets/images/只要图片放在assets/images/目录下Flutter 就可以正常加载。这里有一个细节需要注意文件名最好不要带空格比如不要写成transparent_earphones .png建议统一成transparent_earphones.png避免 Flutter asset 路径出现不必要的问题。四、一次失败尝试用 HTML 还原视觉稿中间也尝试过一种方式先根据目标 UI 图片生成一份高保真的 HTML CSS 静态页面再参考 HTML 的结构和样式去改 Flutter。这个方法在视觉分析阶段有一定帮助比如可以快速拆出页面结构、颜色、字号、按钮圆角、背景装饰等。但是实际接入 Flutter 时效果并不理想。主要原因是 HTML 页面更多是“静态视觉还原”而 Flutter 项目里已经有现成的夜间模式布局、主题切换、按钮交互和页面结构。如果直接按 HTML 方式迁移很容易出现几个问题Flutter 页面比例和 HTML 设计稿不一致。耳机素材仍然受到图片透明度影响。背景波纹、按钮、耳机位置很难直接对应。容易破坏原本已经定好的夜间模式布局。最后效果更像“重新写了一个页面”而不是在原页面上适配白天模式。所以这个方案没有作为最终主方案只保留了它的参考价值HTML 可以用来确认视觉方向但不适合作为最终 Flutter 实现依据。最终还是回到 Flutter 原生实现路线夜间模式保持原样白天模式单独做主题隔离耳机使用真正透明 PNG背景和 logo 用轻量绘制控制。五、白天模式实现思路1. 背景白天模式背景不再强依赖bg_wave.png图片而是通过 Flutter 绘制顶部浅蓝灰到白色渐变很淡的弧形波纹右上角点阵少量浅蓝小光点耳机后方淡蓝波纹底部轻微曲线装饰。这样可以避免背景图本身带白底、灰底或被拉伸变形的问题。白天模式的背景重点不是“蓝”而是“淡”。蓝色只作为局部点缀不能铺满整个页面。2. Logo之前的logo_icon.png不是一个干净的 logo 图而是一张展示图里面有多个 logo、棋盘格、白底和文件名。所以白天模式顶部 logo 不再直接使用这张异常图片而是用CustomPaint画一个简洁的小图标蓝色圆点三段声波弧线小巧居中没有白底没有多余文字不会显示图片文件名或异常画布。3. 耳机主视觉白天模式耳机只使用透明 PNGImage.asset( assets/images/transparent_earphones.png, )这里有一个重点耳机图片外层不加背景色、不加 Card、不加白色底板、不加灰色底板。如果图片本身是真透明那么它就可以自然悬浮在浅蓝灰背景和波纹之上。如果图片本身不透明再怎么用 Flutter 包 Container、ClipRRect、Opacity都只是掩盖问题最后一定会出现矩形底板或者发灰的问题。六、页面结构白天模式 Welcome 页仍保持原来的结构顶部小 logo OUCCL Welcome 短横线 Smart listening. Beautifully connected. 耳机主视觉 Get Started Sign in没有新增功能模块也没有修改交互逻辑。Get Started仍然负责进入主流程Sign in仍然作为次级登录入口。七、关键代码方向耳机区域的实现思路大致如下Stack( alignment: Alignment.center, children: [ Positioned.fill( child: CustomPaint( painter: _LightHeroWavePainter(...), ), ), Image.asset( assets/images/transparent_earphones.png, width: earphoneImageWidth, height: earphoneImageHeight, fit: BoxFit.fill, ), ], )这里没有强制保持图片原始等比例。原因是这个耳机图本身有较大的透明留白如果完全使用BoxFit.contain在页面里的视觉大小可能会偏小。实际处理时可以在白天模式下单独设置耳机图片的显示宽高让它在视觉上接近目标效果即可。但需要注意可以轻微非等比缩放 不要拉伸到明显变形 不要裁切 不要加背景容器 不要出现矩形底板 只在 light mode 下生效。夜间模式仍然保留原来的耳机绘制方式不受白天模式影响。八、按钮和文字处理白天模式的文字颜色主要分三层主标题深蓝黑 副标题灰蓝色 按钮文字白色例如OUCCL使用深蓝黑字距略大Welcome使用深蓝黑字重较高Smart listening. Beautifully connected.使用灰蓝色短横线使用浅蓝渐变Get Started使用柔和亮蓝胶囊按钮Sign in使用蓝色文字和小箭头。按钮不能过于科技蓝也不能发光太重。整体还是要保持浅蓝灰、干净、轻盈的智能硬件风格。九、light / dark 隔离这次改造中最重要的一点是白天模式和夜间模式必须隔离。夜间模式已经定稿所以不能因为白天模式改造影响原来的夜间效果。实现上可以通过isLight判断if (isLight) { // 白天模式使用透明 PNG 耳机 浅蓝灰背景 } else { // 夜间模式继续使用原来的深色霓虹效果 }需要特别避免把白天模式耳机影响到夜间模式。把白天模式背景渐变影响到夜间模式。改动Get Started和Sign in的跳转逻辑。为了还原白天模式而重构整个页面。本次改造应该是“在原页面上适配白天主题”而不是重新写一个 Welcome 页。十、最终效果目标最终白天模式的目标是浅蓝灰白色渐变背景干净、轻盈、柔和透明白色耳机自然悬浮淡蓝波纹和点阵作为装饰深蓝黑文字灰蓝色标语柔和蓝色胶囊按钮Sign in作为蓝色次级入口。整体方向是智能硬件产品欢迎页而不是普通白底页面也不是夜间模式的浅色换皮。十一、经验总结这次最大的教训是UI 效果差不一定是代码布局的问题。如果素材本身不合格后面再怎么调布局和颜色效果都很难好。尤其是透明耳机这种主视觉如果素材不是透明 PNG就很容易出现白色矩形底板 灰色背景块 图片像直接贴上去 背景和主体不融合正确做法应该是先检查素材是否合格。图片该透明就必须真的透明。背景装饰尽量独立绘制或者使用真正透明纹理。不要用代码强行修坏素材。夜间模式已定稿时白天模式要严格隔离实现。HTML 可以辅助分析视觉但不应直接决定 Flutter 的最终实现方式。这次改造后白天模式 Welcome 页的实现路线更清晰耳机用透明 PNG 背景和 logo 用轻量绘制 颜色通过主题隔离 布局和交互保持原有逻辑 夜间模式不受影响。这样既能接近目标视觉效果也能减少对原有代码和夜间模式的破坏。