Bootstrap

Flutter问题记录 - 布局中莫名其妙的白线/缝隙


前言

最近客服反馈了一个奇怪的问题,有个用户反馈其他问题时给了应用截图,然后他发现这截图中有一条奇怪的白线。他在自己手机上没有发现这个问题,于是提工单反馈到我这。

开发环境

  • Flutter: 3.24.3

问题描述

应用截图原图没办法给出,白线大概长这样(不是很明显,图片上传被压缩后可能更不明显):

screenshot1

白线分割的上下两部分,分别是两个Container组件。

问题分析

首先这个问题在公司一堆测试机中都没有发现,查了该用户的登录设备型号是:Xiaomi 23116PN5BC,也就是小米14 Pro

找同型号的云真机测试一番,确实存在这个问题,但是在其他很多手机上都没有发现这个问题,初步判断应该是跟设备屏幕分辨率有关。小米14 Pro分辨率信息如下:

物理分辨率:1440x3200
逻辑分辨率:411x898
设备像素比(devicePixelRatio):2.625

物理分辨率是网上查的,其他的通过Flutter获取。奇怪,devicePixelRatio好像不太对,有点太小了。一般来说:

物理分辨率 = 逻辑分辨率 * 设备像素比

411 * 2.625 = 1078.875,这相差的有点多,这设备像素比更像是小米 14(物理分辨率:1080x2400)的。

这设备像素比合不合理另说,先找一个逻辑分辨率和设备像素比差不多的模拟器用于本地跑demo测试,同时也可以进一步验证这个问题是不是只在小米14 Pro上出现。

这个网站可以查看iPhone/Pixel/Galaxy的大部分设备分辨率信息,找到一个比较符合要求的Google Pixel 7

screenshot2

直接新建一个Google Pixel 7模拟器,再写个测试用的demo:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text(
            'Flutter问题记录 - 布局中莫名其妙的白线/缝隙',
            style: TextStyle(fontSize: 18),
          ),
        ),
        body: SingleChildScrollView(
          child: Column(
            children: [
              Container(
                color: Colors.purple,
                height: 100,
              ),
              Container(
                color: Colors.purple,
                height: 100,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

运行后白线出现了,截图请看前面的问题描述。现在可以确定这问题不局限于小米14 Pro,再仔细看截图中的白线高度明显小于1个逻辑像素,基本可以判断这是由于逻辑像素转为设备像素后出现小数取整导致的问题,毕竟物理像素不能是小数。

实测将第一个Container的高度从100逐渐增加到104,白线的粗细会不断变化,直到104时白线消失。这应该和Flutter的取整方式以及抗锯齿处理有关,由于组件边缘像素对齐误差的变化,被渲染为背景色的区域大小也在不断变化,从而造成粗细不一的白线(如果背景色是白色的)。

100 * 2.625 = 262.5
101 * 2.625 = 265.125
102 * 2.625 = 267.75
103 * 2.625 = 270.375
104 * 2.625 = 273

那该怎么解决这个问题呢?最简单的方法:

View.of(context).devicePixelRatio == 2.625 ? 104 : 100;

根据设备像素比返回不同的逻辑像素,避免得到的物理像素是小数。虽然看上去有点不靠谱,但是简单有效,毕竟这个问题也不是那么容易遇到的,它需要满足以下条件:

  • 两个背景颜色相同的组件放在一起,颜色不同基本发现不了
  • 设备像素比是2.625这种,像iPhone设备像素比全是整数根本不会出现该问题,大部分Android设备也不会出现。开发时逻辑像素尽量使用偶数,这样就算设备像素比是3.5的Android设备得到的物理像素也基本是整数

当然,也有更靠谱一点的解决方式:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text(
            'Flutter问题记录 - 布局中莫名其妙的白线/缝隙',
            style: TextStyle(fontSize: 18),
          ),
        ),
        body: SingleChildScrollView(
          child: Column(
            children: [
              Container(
                // color: Colors.purple,
                height: 100,
                decoration: BoxDecoration(
                  color: Colors.purple,
                  border: Border.all(
                    width: 0,
                    color: Colors.purple,
                  ),
                ),
              ),
              Container(
                // color: Colors.purple,
                height: 100,
                decoration: BoxDecoration(
                  color: Colors.purple,
                  border: Border.all(
                    width: 0,
                    color: Colors.purple,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

设置宽度为0的边框起到像素对齐的作用。注意,都需要设置,不然很可能还会有白线。

这问题也不是什么新鲜问题,目前也没有完美的解决方案,相关的issue很多,例如:Antialiasing behaviour when same-colour

解决方案

  1. 对特定设备像素比适配,参考如下:
View.of(context).devicePixelRatio == 2.625 ? 104 : 100;
  1. 给组件设置宽度为0的边框,参考如下:
Container(
  // color: Colors.purple,
  height: 100,
  decoration: BoxDecoration(
    color: Colors.purple,
    border: Border.all(
      width: 0,
      color: Colors.purple,
      ),
    ),
)

最后

如果这篇文章对你有所帮助,点赞👍收藏🌟支持一下吧,谢谢~


本篇文章由@crasowas发布于CSDN。

;