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

相关推荐
豆豆豆大王3 小时前
Android studio图像视图和相对布局知识点
android·ide·android studio
我命由我123454 小时前
Android 实例 - Android 圆形蒙版(Android 圆形蒙版实现、圆形蒙版解读)
android·java·java-ee·android studio·安卓·android-studio·android runtime
猪哥帅过吴彦祖4 小时前
Flutter 系列教程:布局基础 (上) - `Container`, `Row`, `Column`, `Flex`
前端·flutter·ios
天若有情6734 小时前
【Android】Android项目目录结构及其作用
android
灿烂阳光g4 小时前
Android Automotive OS架构
android
星海浮沉6 小时前
flutter AudioPlayer的使用问题及处理
flutter
-晨-风-6 小时前
Flutter 运行IOS真机,提示无法打开“****”
flutter·ios
zhifanxu6 小时前
flutter mixin
flutter
一碗情深6 小时前
Android 开发环境解析:从SDK、NDK到版本兼容性指南
android·安卓·sdk·ndk