如何通过flutter地图生成静态图片进行展示

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

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

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

老规矩: 先贴Demo

Plan A

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

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

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

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

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

效果如下:

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

but

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

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

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

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

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

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

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

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

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

Plan B

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

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

1.Flutter系统截图

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

less 复制代码
RepaintBoundary(
  key: containerKey,
  child: YourMapWidget(),
)

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

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

2.地图自带截图功能

思路

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

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

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

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

scss 复制代码
/// 进行截图
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");
  }
}

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

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

效果:

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

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

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

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

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

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

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

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

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

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

    ini 复制代码
      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
  • 如果对性能要求不高,那就直接使用地图控件把
相关推荐
Lanren的编程日记4 小时前
Flutter鸿蒙应用开发:生物识别(指纹/面容)功能集成实战
flutter·华为·harmonyos
Lanren的编程日记7 小时前
Flutter鸿蒙应用开发:基础UI组件库设计与实现实战
flutter·ui·harmonyos
西西学代码8 小时前
Flutter---波形动画
flutter
于慨11 小时前
flutter基础组件用法
开发语言·javascript·flutter
恋猫de小郭13 小时前
Android CLI ,谷歌为 Android 开发者专研的 AI Agent,提速三倍
android·前端·flutter
火柴就是我14 小时前
flutter pushAndRemoveUntil 的一次小疑惑
flutter
于慨14 小时前
flutter doctor问题解决
flutter
唔6615 小时前
flutter 图片加载类 图片的安全使用
安全·flutter
Nathan2024061616 小时前
Flutter - InheritedWidget
flutter·dart
恋猫de小郭16 小时前
JetBrains Amper 0.10 ,期待它未来替代 Gradle
android·前端·flutter