likes
comments
collection
share

flutter静态地图展示优化

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

最近项目里需要用到地图展示,但是仅仅是展示,不需要进行交互处理,效果如下:

flutter静态地图展示优化

针对这种场景,比较常见的用法是使用简单的地图控件,屏蔽掉交互事件就可以了。

但是实际使用发现,可以是可以,性能不是很好,内存占用比较高,而且页面拖拽的时候有时候会出现兼容闪烁问题,于是就想着怎么去优化这一块,最终生成了以下两种方案,有相同烦恼的童鞋可以参考参考。

老规矩: 先贴Demo

Plan A

首先我们使用的是谷歌地图,通过查阅官方的文档发现,谷歌其实已经提供了我们需要的功能,支持一键生成静态地图的功能:Maps Static API

通过指定链接可以获取到对应经纬度位置的图片

maps.googleapis.com/maps/api/st…

链接支持配置地图类型、地区、语言、缩放系数、尺寸、大头针图标、路径轨迹、图片类型等等等等,功能应有尽有,基本上是能满足各种日常的业务场景的。

你需要做的仅仅是通过官方进行注册,获取一个特有的mapkey就好了,其他的就交给谷歌吧。

效果如下:

flutter静态地图展示优化

听起来简直不要太完美有木有

flutter静态地图展示优化

but

-------------转折线---------------

美好的事物的获取总是不那么容易

唯一的一个缺陷收费 (划重点)

费用是根据浏览量来计算的,对于日活较多的项目来说,还是会有一定的费用

当然,财大气粗的童鞋可以忽略这一点,直接用,不差钱。

另外,一些偏小众,或日活不那么多的项目也可以放心使用,一段时间内低于一定浏览量也是完全免费的。

有兴趣的同学可以查阅下官方文档的收费标准

对于我们的项目来说,日活不低,为了白嫖,哦不,是为了给公司开源节流,杜绝养成走捷径的不良风气,经过慎重考虑 QAQ,我们毅然地舍弃了这个方案。

那么,我们该继续忍受原生地图的折磨吗?不,让我们接着往下看

Plan B

排除了图片链接方案后,我们尝试着手动将地图转化为图片。

首先我们尝试了使用dart原生截图方法对地图进行处理,生成图片

1.Flutter系统截图

在flutter项目中使用RepaintBoundary组件可以实现局部截图功能

RepaintBoundary(
  key: containerKey,
  child: YourMapWidget(),
)

但是对地图实践发现截取出来的是一张空白的图

这是因为谷歌地图是在原生平台上绘制的,使用RepaintBoundary对其进行截取是不可行的

2.地图自带截图功能

思路

谷歌地图GoogleMap提供了takeSnapshot方法用于截图

flutter静态地图展示优化

Apple地图AppleMap也提供了相似的方法进行处理

flutter静态地图展示优化

实际上在上述方法内部也就是通过原生进行截图处理的

通过该方法我们可以拿到对应截图的字节流数据,从而转化为对应的图片

/// 进行截图
void startSnapShot() async {
  debugPrint("地图开始截屏");
  try {
    final uin8list = await state.mapController?.takeSnapshot();
    if (uin8list != null) {
      state.mapImage = MemoryImage(uin8list);
      update();
    } else {
      debugPrint("地图截屏失败");
    }
  } catch (e) {
    debugPrint("地图截屏错误:$e");
  }
}

拿到图片之后将现有的布局进行刷新,替换掉地图控件,并进行释放,就可以实现正常的地图图片展示了,内存正常释放,并且不会再出现上面提到的兼容闪烁问题。

/// controller释放
void releaseMapController() {
  debugPrint("地图controller释放");
  state.mapController?.dispose();
  state.mapController = null;
}

效果:

谷歌地图(上面是地图控件,下面是截图)

flutter静态地图展示优化

苹果地图 (上面是地图控件,下面是截图)

flutter静态地图展示优化

问题
  • 地图在数据未加载完毕,出现图像之前进行截图,会得到一张空白的图片

    针对这个问题,通过查阅Api发现地图控件并未提供加载完毕的回调事件,也就是说我们没有很好的办法来监听地图的数据加载

    不过在实践中发现,除了网速很差的情况,一般地图都能在零点几秒内完成加载

    所以在这里我采用了延时1秒左右进行截图的方案

      Future.delayed(
        const Duration(milliseconds: 1000),
        () async {
          logic.startSnapShot();
        },
      );
    
  • 地图未出现在当前屏幕中,进行截图会得到一张空白的图片

    这就要求我们对地图的出现进行监听,当它出现在屏幕中才能进行截图操作

    此外,还需要考虑到上面提到的数据加载延时问题,

    最终的方案是使用VisibilityDetector组件对地图可见度进行监听,当地图第一次出现时,延时进行截图,如果期间地图滑出屏幕,则不进行处理。当地图再次进入屏幕,则不需要延时,立即进行截图处理。

      return VisibilityDetector(
        key: state.mapKey,
        onVisibilityChanged: (VisibilityInfo info) async {
          state.mapVisibleFraction = info.visibleFraction;
          // debugPrint("地图可见度:${info.visibleFraction}");
          // 地图第一次出现的时候,延时0.6s后才进行截图,避免地图未渲染完毕,截图空白
          if (info.visibleFraction > 0 && state.isMapFirstShow) {
            state.isMapFirstShow = false;
            Future.delayed(
              const Duration(milliseconds: 600),
              () async {
                state.isMapFinishLoad = true;
                if (state.mapVisibleFraction > 0.2) {
                  logic.startSnapShot();
                }
              },
            );
          }
          if (info.visibleFraction < 0.2) return;
          if (state.mapController == null ||
              !state.isMapFinishLoad ||
              state.isMapWaitSnapShot) return;
          logic.startSnapShot();
        },
        child: GoogleMap(
        ...
        ),
      );
    
  • 3.安卓端使用谷歌地图进行截图处理时,在极端场景下,触发了截图之后立刻跳转屏幕,可能会出现crash问题

    这个跟地图SDK内部原生的处理有关,目前没有好的解决方案,只能等官方出手了,大家用的时候可以实际测试一下

总结

  • 追求简单易用性,或日活不大的App可以使用谷歌静态地图来实现
  • 不符合第一条条件的可以使用上述的截图方案,详细代码可以参考Demo
  • 如果对性能要求不高,那就直接使用地图控件把
转载自:https://juejin.cn/post/7297159273905848361
评论
请登录