likes
comments
collection
share

Flutter Web - 如何适配 iphone 安全区域

作者站长头像
站长
· 阅读数 15

Flutter Web 系列

《Flutter Web - 让 Web 与 APP UI 一致的另一种可能》 

《# Flutter Web - 优雅的兼容 Flutter App 代码》

(提前预警,本文多图)

昨日正在将 Flutter Web 的实现的页面放入微信小程序中看下效果,突然发现在 iphoneX 设备上,底部安全区域没有适配成功,如下图:

Flutter Web - 如何适配 iphone 安全区域

排查过程

作为一个标准流程的程序员,出问题了都是要先自我怀疑一遍。

检查 Flutter 代码

Flutter Web - 如何适配 iphone 安全区域

显式声明了 SafeArea,再没什么特别的了

检查微信小程序代码

公司的小程序代码比较骚一点,用 taro 实现的。

Flutter Web - 如何适配 iphone 安全区域

想当然的搜了下前端处理 iphone 安全区域的文章,都是推荐增加样式即可。

@supports (bottom: constant(safe-area-inset-bottom)) or
  (bottom: env(safe-area-inset-bottom)) {
  webview {
    margin-bottom: constant(safe-area-inset-bottom);
    margin-bottom: env(safe-area-inset-bottom);
  }
}

然而并没有什么卵用,又查了下 taro 的开发文档也没有什么特别的介绍。

最后查小程序文档时,找到这么一句

Flutter Web - 如何适配 iphone 安全区域

看起来是不能在小程序侧解决这个问题的。

尝试在 Web 上解决

那我们能不能在 Web 侧解决问题,毕竟 Flutter Web 终究还是 Web,在 index.html 加个 css 样式不就行了。

但并不可行,

Flutter Web - 如何适配 iphone 安全区域

强行对渲染的标签增加 margin-bottom: 30px;

结果令人尴尬

Flutter Web - 如何适配 iphone 安全区域

标签确实顶上去了,但内容没变化 [猝死]。

想了想原因,应该是 Flutter 在实现 Web 上大都是使用的绝对定位(具体是如何实现的没有去源码里找)。

解决方式

自我怀疑结束,那就官方寻找解决方案了。git 不欺我,搜索一下 safeArea 还是很容易找到 issues 的。issues 传送门

Flutter Web - 如何适配 iphone 安全区域

这问题竟然都 open 一年半了,还是 P3 级的,Flutter 官方竟然一直都不解决,这是多瞧不起 iOS safari [狗头*2]。

好在下面评论里有人发了解决方案,也有一个可用的 npm 包 safeAreaInsets

但本着不乱引用三方库的习惯,看了它源码,也十分简单,就一个文件,具体原理就是构造一个绝对定位的 div 然后增加 safe-area-inset-bottom CSS,再通过 JS 去获取这个 divtop , bottom 即可。

那就没必要引入 npm 包了,直接改成一个工具类拿来即用。

也没必要手写评论里的 Flutter TS 链接类。

Flutter Web - 如何适配 iphone 安全区域

我们有 TS2dart 工具,想要工具的同学可以看下这篇 《# Vite + Flutter:打造不一样的前端框架(附源码)》

api.ts

export class GDGlobal {
  
  /**
   * 安全区域
   */
  static safeAreaInsets: GDSafeAreaInsets
}

...

/**
 * 安全区域
 */
export interface GDSafeAreaInsets {
  /**
   * 上
   */
  top: number
  /**
   * 下
   */
  bottom: number
  /**
   * 左
   */
  left: number
  /**
   * 右
   */
  right: number
  /**
   * 是否支持
   */
  support: boolean
}

TS 定义一下,这样就可以直接在 Flutter 项目里使用了。

  SafeArea( // 还是保留了 SafeArea
    child: Container( // 增加一个 Container 用于增加 padding
      padding: EdgeInsets.only(
        // 直接调用即可 
        bottom: GDGlobal.safeAreaInsets.bottom.toDouble(),
      ),
      child: BottomNavigationBar(
    ),
    ...
  ),
  

好了,我们再试一下,还是没生效,WTF??

想想 safe-area-inset-bottom 生效的必要条件,想到是因为项目里没加 viewport-fit=cover,如果是 Vue 开发的移动端项目一开始就会加,但 Flutter Web 上总是有些疏漏。

index.html

<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    ...

Flutter Web - 如何适配 iphone 安全区域

可以了,底部栏总算是在安全区域内了。

BottomNavigationBar 这官方组件竟然自带底部阴影,也是我没想到的 ...

Flutter Web - 如何适配 iphone 安全区域

解决也很简单,增加 elevation: 0 即可。

总结

为什么 Flutter 官方1年半也不解决这个 P3 级问题?笔者想了下原因,大概是 Flutter 不希望使用 Web 的解决方式。一旦是桌面模式的 Web 页面,也不能乱加 viewport-fit=cover

而且受众面确实很小,本来 Flutter web 的项目就不多,而且在手机浏览器上都是有底部操作栏的,除非是笔者这样用 web-view 嵌套在小程序或者“添加到主屏幕”这样的方式 ...

转载自:https://juejin.cn/post/7177192190794268728
评论
请登录