flutter 3.22+ Android集成高德Flutter地图自定义Marker显示

在Flutter 3.0.1版本中,高德Flutter地图自定义Marker显示的具体方法在

https://blog.csdn.net/wz_1992/article/details/127117864?spm=1011.2415.3001.5331

近期将Flutter 升级到3.22.3之后,工具类RenderView的用法发生了改变,

之前:

kotlin 复制代码
RenderView renderView = RenderView(
      child: RenderPositionedBox(alignment: alignment, child: repaintBoundary),
      configuration: ViewConfiguration(
        size: size,
        devicePixelRatio: devicePixelRatio,
      ),
      window: ui.window,
    );

已经不适用,RenderView中新增了view字段,去掉了window字段,且ViewConfiguration里面去掉了size字段

我将RenderView用法修改为:

kotlin 复制代码
 final renderView = RenderView(
      view: WidgetsBinding.instance.platformDispatcher.views.first,
      child: RenderPositionedBox(
        child: repaintBoundary,
      ),
      configuration: ViewConfiguration(
        devicePixelRatio: WidgetsBinding
            .instance.platformDispatcher.views.first.devicePixelRatio,
      ),
    );

之后,发现widget(buildShiMarkWidget())一直无法成功转为image,

查询了在资料后,换了种处理方式:

build方法代码如下:

重点是Positioned,先把需要转为image的布局写在build中,放在stack里面,被map遮挡,不让他显示(因为我是要展示多个marker所以这里用了个ListView)

重点是使用:

每个Marker都需要一个GlobalKey

kotlin 复制代码
  final List<GlobalKey> _widgetKeyShiList = [];
kotlin 复制代码
@override
  Widget build(BuildContext context) {
    ///使用默认属性创建一个地图
    map = AMapWidget(
        privacyStatement: ConstConfig.amapPrivacyStatement,
        apiKey: ConstConfig.amapApiKeys,
        //地图样式  默认普通地图 普通视图 normal,卫星视图 satellite, 夜间视图 night, 导航视图 navi, 公交视图 bus,
        mapType: MapType.normal,
        //是否显示3D地图
        buildingsEnabled: false,
        //设置地图默认中心点
        initialCameraPosition:
            CameraPosition(target: LatLng(_centerLat, _centerLng), zoom: 8),
        //是否显示指南针
        compassEnabled: false,
        //比例尺是否显示
        scaleEnabled: true,
        //是否显示路况信息
        trafficEnabled: false,
        //是否显示底图文字
        labelsEnabled: true,
        //旋转手势
        rotateGesturesEnabled: true,
        //滑动手势
        scrollGesturesEnabled: true,
        //倾斜手势
        tiltGesturesEnabled: true,
        //缩放手势
        zoomGesturesEnabled: true,
        //地图创建完成回调,成功后会返回AMapController对象
        onMapCreated: onMapCreated,
        //地图移动回调
        // onCameraMove: _onCameraMove,
        onCameraMoveEnd: _onCameraMoveEnd,
        // //创建地图时,给marker属性赋值一个空的set,否则后续无法添加marker
        markers: Set<Marker>.of(_markers.values),
        myLocationStyleOptions: _myLocationStyleOptions);
    return Scaffold(
        body: Stack(children: [
      /*创建市的marker 创建但是不显示,这样RenderRepaintBoundary才可以将widget渲染为图片*/
      Positioned(
        // left: -500, // 移出可视区域
        child: Container(
          child: ListView.builder(
              shrinkWrap: true,
              itemBuilder: (context, index) {
                return buildShiMarkWidget(index, _widgetKeyShiList[index]);
              },
              itemCount: mCityData.length),
        ),
      ),
      map,
    ]));
  }

自定义Marker的Widget代码如下:

kotlin 复制代码
 //Mark样式-市
  /* Future<Widget>*/
  Widget buildShiMarkWidget(int index, GlobalKey _widgetKey) /*async*/ {
    //带图片的时候需要先把图片缓存一下,否则不显示
    // AssetImage provider = AssetImage('assets/images/oil_sheng_marker.png');
    // await precacheImage(provider, context);
    return RepaintBoundary(
        key: _widgetKey,
        child: Container(
            alignment: Alignment.center,
            width: 20,
            height: 24,
            decoration: BoxDecoration(
              image: DecorationImage(
                  image: AssetImage('assets/images/oil_sheng_marker.png')),
            ),
            child: Container(
              padding: EdgeInsets.only(bottom: 5),
              child: Directionality(
                  textDirection: TextDirection.ltr,
                  child: Text(mCityData[index].num.toString(),
                      style: TextStyle(
                          color: Colors.white,
                          fontSize: 10,
                          fontWeight: FontWeight.bold))),
            )));
  }

高德添加marker的方法:

kotlin 复制代码
///添加marker  先创建好 放在容器中
  Future<void> _addMarker(
      String type, LatLng mLatLng, int index, bool needshow) async {
    final _markerPosition = mLatLng;
    Uint8List? bd = await _captureWidget( _widgetKeyShiList[index]);
    final Marker marker = Marker(
        position: _markerPosition,
        infoWindowEnable: false,
        //使用默认hue的方式设置Marker的图标
        icon: BitmapDescriptor.fromBytes(bd!),
        // icon:  BitmapDescriptor.fromIconPath('image/distract_bg.png')
        onTap: (id) {
          currentMarkerId = id;
          if (type == "市") {
            setState(() {
              _changeCameraPosition(11, mLatLng);
            });
          } else if (type == "区") {
            setState(() {
              //记录要查询的油站位置的所属区信息
              mCountryCode = mAreaData[index].code;
              getAreaGasData();
              showMarkerType = MARKER_GAS;
              _changeCameraPosition(14, mLatLng);
            });
          }
        });
    if (type == "市") {
      setState(() {
        mShiMarkers.add(marker);
      });
    } else if (type == "区") {
      setState(() {
        mAreaMarkers.add(marker);
      });
    }
    //调用setState触发AMapWidget的更新,从而完成marker的添加
    if (needshow) {
      setState(() {
        //将新的marker添加到map里
        _markers[marker.id] = marker;
      });
    }
  }

将Widget转为Image的方法_captureWidget()

kotlin 复制代码
 Future<Uint8List?> _captureWidget(GlobalKey _widgetKey) async {
    await Future.delayed(const Duration(milliseconds: 50));

    try {
      final RenderRepaintBoundary boundary = _widgetKey.currentContext!
          .findRenderObject() as RenderRepaintBoundary;
      final ui.Image image = await boundary.toImage(pixelRatio: 3.0);
      final ByteData? byteData =
          await image.toByteData(format: ui.ImageByteFormat.png);
      return byteData?.buffer.asUint8List();

      image.dispose();
    } catch (e) {
      debugPrint('Capture error: $e');
    } finally {}
  }

有的资料说,如果自定义Widget中带有图片的话需要先将图片缓存一下,附带下图片缓存的方法:
_cacheImg()方法我是在initState()里面引用的

kotlin 复制代码
/*缓存图片*/
  Future<void> _cacheImg() async {
    //带图片的时候需要先把图片缓存一下,否则不显示
    _precacheWithListener('assets/images/oil_sheng_marker.png');
    //_precacheWithListener('assets/images/oil_qu_marker.png');
  }

  Future<void> _precacheWithListener(String _assetPath) async {
    print('开始资源图片缓存${_assetPath}...');
    try {
      final imageProvider = AssetImage(_assetPath);
      final completer = Completer<void>();

      final stream = imageProvider.resolve(ImageConfiguration.empty);
      final listener = ImageStreamListener(
        (ImageInfo image, bool sync) {
          print('${_assetPath}资源缓存完成');
          completer.complete();
          setState(() {});
        },
        onError: (exception, stackTrace) {
          print('${_assetPath}资源缓存失败');
          completer.completeError(exception, stackTrace);
        },
      );
      stream.addListener(listener);
      await precacheImage(imageProvider, context);
      await completer.future;
      stream.removeListener(listener);
    } catch (e) {
      print('${_assetPath}缓存发生错误');
    }
  }

虽然这个方法在最后实现了需求,但是给我的感觉就是很乱,东拼西凑硬是给跑起来了,应该还有更好的实现方法,之后会再做补充

高德地图使用:

kotlin 复制代码
  #高德地图
  amap_flutter_map: ^3.0.0

备注:amap_flutter_map: ^3.0.0 高德平台已经不再维护更新

相关推荐
晚霞的不甘24 分钟前
Flutter for OpenHarmony 可视化教学:A* 寻路算法的交互式演示
人工智能·算法·flutter·架构·开源·音视频
doupoa24 分钟前
内存指针是什么?为什么指针还要有偏移量?
android·c++
千逐6839 分钟前
《Flutter for OpenHarmony:星轨天气的粒子化气象宇宙可视化系统》
flutter
非凡ghost1 小时前
PowerDirector安卓版(威力导演安卓版)
android·windows·学习·软件需求
晚霞的不甘2 小时前
Flutter for OpenHarmony 实现计算几何:Graham Scan 凸包算法的可视化演示
人工智能·算法·flutter·架构·开源·音视频
独行soc2 小时前
2026年渗透测试面试题总结-19(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
千逐682 小时前
气象流体场:基于 Flutter for OpenHarmony 的实时天气流体动力学可视化系统
flutter
一只大侠的侠2 小时前
Flutter开源鸿蒙跨平台训练营 Day12从零开发通用型登录页面
flutter·开源·harmonyos
晚霞的不甘2 小时前
Flutter for OpenHarmony天气卡片应用:用枚举与动画打造沉浸式多城市天气浏览体验
前端·flutter·云原生·前端框架
子春一3 小时前
Flutter for OpenHarmony:语桥 - 基于Flutter的离线多语言短语速查工具实现与国际化设计理念
flutter