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的用法发生了改变,
之前:
RenderView renderView = RenderView(child: RenderPositionedBox(alignment: alignment, child: repaintBoundary),configuration: ViewConfiguration(size: size,devicePixelRatio: devicePixelRatio,),window: ui.window,);
已经不适用,RenderView中新增了view字段,去掉了window字段,且ViewConfiguration里面去掉了size字段
我将RenderView用法修改为:
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
final List<GlobalKey> _widgetKeyShiList = [];
@overrideWidget 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,否则后续无法添加markermarkers: 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代码如下:
//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的方法:
///添加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()
:
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()里面引用的
/*缓存图片*/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}缓存发生错误');}}
虽然这个方法在最后实现了需求,但是给我的感觉就是很乱,东拼西凑硬是给跑起来了,应该还有更好的实现方法,之后会再做补充
高德地图使用:
#高德地图amap_flutter_map: ^3.0.0
备注:amap_flutter_map: ^3.0.0 高德平台已经不再维护更新