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 高德平台已经不再维护更新

相关推荐
火柴就是我5 小时前
让我们实现一个更好看的内部阴影按钮
android·flutter
王晓枫5 小时前
flutter接入三方库运行报错:Error running pod install
前端·flutter
砖厂小工11 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心12 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心12 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
shankss13 小时前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
Kapaseker15 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴15 小时前
Android17 为什么重写 MessageQueue
android
忆江南1 天前
iOS 深度解析
flutter·ios
明君879971 天前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter