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发布于优快云。

在iOS打包过程中,出现的Sandbox权限错误通常与代码签名或文件访问权限有关。具体来说,当`dart`进程尝试读取`flutter_lldbinit`文件时,系统会因为Sandbox限制而阻止该操作。此类问题可能由以下原因导致: 1. **文件路径问题**:`flutter_lldbinit`文件可能位于受限目录中,例如`/Users/glaze/Documents/flutter_deer-master/ios/Flutter/ephemeral/`。此目录通常用于临时文件存储,某些情况下Sandbox策略会阻止对这些文件的访问[^1]。 2. **代码签名问题**:iOS应用在打包时需要进行严格的代码签名验证。如果签名配置不正确或签名证书不匹配,可能会导致Sandbox限制阻止某些文件的访问。例如,调试符号文件(如`.dSYM`)或临时生成的文件(如`flutter_lldbinit`)可能无法被正确访问。 3. **Xcode配置问题**:Xcode的构建设置(如`Build Settings`中的`Code Signing Identity`和`Provisioning Profile`)如果没有正确配置,也可能导致Sandbox权限错误。 ### 解决方案 #### 1. 清理构建缓存 有时,Xcode或Flutter工具链生成的缓存文件可能会导致问题。可以尝试清理构建缓存并重新构建项目: ```bash flutter clean cd ios rm -rf Pods/ build/ Podfile.lock build DerivedData cd .. flutter build ios ``` #### 2. 检查代码签名配置 确保Xcode项目中的代码签名配置正确。打开`ios/Runner.xcworkspace`,进入`Signing & Capabilities`选项卡,检查以下内容: - `Team`是否正确设置。 - `Provisioning Profile`是否匹配当前设备和证书。 - `Code Signing Identity`是否设置为正确的证书。 #### 3. 检查文件访问权限 确认`flutter_lldbinit`文件的权限是否正确。可以通过以下命令修改文件权限: ```bash chmod 644 /Users/glaze/Documents/flutter_deer-master/ios/Flutter/ephemeral/flutter_lldbinit ``` #### 4. 禁用调试符号生成(可选) 如果问题与调试符号文件有关,可以尝试禁用调试符号生成。在Xcode中,进入`Build Settings`,找到`Generate Debug Symbols`并将其设置为`NO`。 #### 5. 更新Flutter和Xcode 确保使用的Flutter版本和Xcode版本兼容。可以通过以下命令更新Flutter: ```bash flutter upgrade ``` 同时,确保Xcode已更新到最新版本。 #### 6. 检查Sandbox配置 如果上述方法无效,可以尝试手动调整Sandbox配置。在Xcode中,进入`Signing & Capabilities`选项卡,添加`App Sandbox`功能,并确保相关文件路径的访问权限已启用。 ### 示例代码:修改文件权限 以下是一个修改文件权限的示例代码: ```bash # 修改flutter_lldbinit文件权限 chmod 644 /Users/glaze/Documents/flutter_deer-master/ios/Flutter/ephemeral/flutter_lldbinit ``` ### 示例代码:清理构建缓存 以下是清理构建缓存的示例代码: ```bash # 清理Flutter缓存 flutter clean # 进入iOS目录并清理缓存 cd ios rm -rf Pods/ build/ Podfile.lock build DerivedData cd .. # 重新构建iOS项目 flutter build ios ``` ### 示例代码:检查代码签名配置 以下是在Xcode中检查代码签名配置的步骤: 1. 打开`ios/Runner.xcworkspace`。 2. 选择`Runner`目标。 3. 进入`Signing & Capabilities`选项卡。 4. 确保`Team`、`Provisioning Profile`和`Code Signing Identity`正确设置。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值