使用本插件,开发者可方便地在其腾讯云 IM Flutter 项目中,引入发送接收展示位置消息能力。本插件提供三个组件:
说明:
上述插件提供为其配套业务逻辑能力,除与地图底层 SDK 交互部分外。
为方便开发者快速接入,我们还提供基于百度地图的完整 Example 代码。具体请查看腾讯云 IM Flutter 或本 Plugin 的 Example。
术语介绍
POI: Point of interesting。即地图上任何非地理意义的有意义的点,每个 POI 均有特定坐标/名称/各级地址/ID。 例如:深圳腾讯大厦,蛇口太子广场。
整体流程
- 选定底层地图 SDK。
- 继承三个抽象类,完成本插件业务代码与地图 SDK 间交互。
- 将继承后的抽象类实例化,传入本插件提供的三个组件中。
选定底层地图 SDK
本插件大部分业务逻辑接口基于百度地图设计,选用百度地图可较快完成项目。但不限制具体使用哪一块地图 SDK,常用地图 Flutter SDK 如下:
开发接入
数据交互数据结构
本插件使用一系列数据结构来定义 SDK 与插件间传递的信息,包含如下:
class LocationMessage {
final String desc;
final double longitude;
final double latitude;
}
/// 代表经纬度
class TIMCoordinate implements TIMLocationBaseModel {
/// 纬度
late double latitude;
/// 经度
late double longitude;
Map<String, Object> toMap();
fromMap(Map map);
}
/// POI信息类
class TIMPoiInfo implements TIMLocationBaseModel {
/// POI名称
String? name;
/// POI坐标
TIMCoordinate? pt;
/// POI地址信息
String? address;
/// POI唯一标识符uid
String? uid;
/// POI所在省份
String? province;
/// POI所在城市
String? city;
fromMap(Map map);
Map<String, Object?> toMap();
}
/// 根据地理坐标反向查询结果类
class TIMReverseGeoCodeSearchResult implements TIMLocationBaseModel {
/// 地址坐标
TIMCoordinate? location;
/// 地址名称
String? address;
/// 层次化地址信息
TIMAddressComponent? addressDetail;
/// 地址周边POI信息,成员类型为BMKPoiInfo
List<TIMPoiInfo>? poiList;
/// 结合当前位置POI的语义化结果描述, 用于地址名称字段。例如"腾讯大厦内,招行信息研发大厦附近18米"。
String? semanticDescription;
fromMap(Map map);
Map<String, Object?> toMap();
}
/// 地址结果的层次化信息
class TIMAddressComponent implements TIMLocationBaseModel {
/// 国家
String? country;
/// 省份名称
String? province;
/// 城市名称
String? city;
/// 区县名称
String? district;
/// 乡镇
String? town;
fromMap(Map map);
Map<String, Object?> toMap()
}
/// 枚举:地图区域改变原因
enum TIMRegionChangeReason {
///<手势触发导致地图区域变化,如双击、拖拽、滑动地图
Gesture,
///<地图上控件事件,如单击指南针返回2D地图。
Event,
///<开发者调用接口、设置地图参数等导致地图区域变化
APIs,
}
/// 用于作为外部导航软件的App信息
class NavigationMapItem{
/// APP 名称
final String name;
/// 唤起外部导航APP的方法
final Function(double longitude, double latitude) jumpFunc;
NavigationMapItem(this.name, this.jumpFunc);
}
继承抽象类,连接地图 SDK 与插件业务逻辑
若选用百度地图,可直接使用我们的完整 Example 代码,快速完成项目。请参加 详细指南。 请根据选定的地图 SDK,继承以下三个类:
TIMMapService
地图定位及 POI 搜索能力 Service。需要根据地图 SDK 完成交互并将数据提供给插件业务代码。
/// 【可选】仅当您需要使用地图SDK提供的定位能力,才需要继承本方法。开关:LocationPicker/LocationShow的isUseMapSDKLocation字段。
/// 需做到根据地图SDK提供的定位能力定位再再通过'moveMapCenter(coordinate)'将地图挪过去,
/// 并返回含根据新的地图中心查询附近POI的结果及是否出错参数的方法
void moveToCurrentLocationActionWithSearchPOIByMapSDK({
required void Function(TIMCoordinate coordinate) moveMapCenter,
void Function(TIMReverseGeoCodeSearchResult, bool)?
onGetReverseGeoCodeSearchResult,
});
/// 根据关键词搜索POI,优先返回在当前city内的结果,但同时也可以搜到其他city的POI
void poiCitySearch({
required void Function(List<TIMPoiInfo>?, bool)
onGetPoiCitySearchResult,
required String keyword,
required String city,
});
/// 根据地理坐标查询附近的POI,并返回包含TIMReverseGeoCodeSearchResult及是否报错参数的方法
void searchPOIByCoordinate(
{required TIMCoordinate coordinate,
required void Function(TIMReverseGeoCodeSearchResult, bool)
onGetReverseGeoCodeSearchResult});
TIMMapWidget
渲染地图的基类,对外暴露地图事件。是 StatefulWidget,需要继承 TIMMapState。 包含地图加载完成回调及地图拖动结束回调。
final Function? onMapLoadDone;
final Function(TIMCoordinate? targetGeoPt, TIMRegionChangeReason regionChangeReason)? onMapMoveEnd;
TIMMapState
渲染地图基类的State,提供完成一系列地图交互能力,并对外返回可直接使用的地图实例。
/// 地图创建完成回调
void onMapLoadDone(){}
/// 地图移动结束
void onMapMoveEnd(TIMCoordinate? targetGeoPt, TIMRegionChangeReason regionChangeReason){}
/// 移动地图视角
void moveMapCenter(TIMCoordinate pt){}
/// 禁用地图交互
void forbiddenMapFromInteract() {}
/// 在地图上添加图钉
void addMarkOnMap(TIMCoordinate pt, String title){}
/// 此处实例化地图
@override
Widget build(BuildContext context) {
return Container(
child: 某地图的Widget(
onMapCreated: onMapCreated,
mapOptions: initMapOptions(),
),
);
}
使用组件
位置选择器(LocationPicker)
该组件以类似微信的位置选择器页面呈现,允许用户定位当前位置/地图选点/搜索特定 POI /展示选定 POI 周边其他 POI。
/// 地理位置选择完成后的onChange事件,返回一个LocationMessage,可用于发送消息。
/// 【特别说明】由于腾讯云IM位置消息仅支持传递一个desc字符串,因此此处的LocationMessage.desc将名称及地址拼接传递,格式:"腾讯大厦/////深圳市南山区深南大道10000号"。
/// 该拼接格式可被本插件所需地方解析,请放心使用。
final ValueChanged<LocationMessage> onChange;
/// 传入根据选定地图SDK实例化后的LocationUtils
final LocationUtils locationUtils;
/// 用于还未加载出来定位时,打开页面后,默认的中心点。
final TIMCoordinate? initCoordinate;
/// 用于控制是否使用地图SDK定位能力。若使用,请确保moveToCurrentLocationActionWithSearchPOIByMapSDK方法继承正确。
final bool? isUseMapSDKLocation;
/// 传入根据选定地图SDK实例化后的地图组件TIMMapWidget
final TIMMapWidget Function(
VoidCallback onMapLoadDone,
Key mapKey,
Function(TIMCoordinate? targetGeoPt,
TIMRegionChangeReason regionChangeReason)
onMapMoveEnd) mapBuilder;
位置消息完整展示器(LocationShow)
该组件以类似微信的位置详情展示页面呈现,使用大地图配上图标,展示接收到的位置。底部显示位置名称/地址,及拉起跳转导航软件的按钮。
/// 位置名称标题
final String addressName;
/// 位置地址
final String? addressLocation;
/// 纬度
final double latitude;
/// 经度
final double longitude;
/// 传入根据选定地图SDK实例化后的LocationUtils
final LocationUtils locationUtils;
/// 用于控制是否使用地图SDK定位能力。若使用,请确保moveToCurrentLocationActionWithSearchPOIByMapSDK方法继承正确。
final bool? isUseMapSDKLocation;
/// 第三方导航APP列表,如果没传,则默认腾讯/百度/高德/苹果地图。
final List<NavigationMapItem>? navigationMapList;
/// 传入根据选定地图SDK实例化后的地图组件TIMMapWidget
final TIMMapWidget Function(
VoidCallback onMapLoadDone,
Key mapKey) mapBuilder;
位置消息列表展示器(LocationMsgElement)
此组件用于在历史消息列表中,展示位置消息。效果类似微信。提供小地图展示/位置名称及地址展示。
/// 消息ID
final String? messageID;
/// V2TimLocationElem消息
final V2TimLocationElem locationElem;
/// 是否自己发送
final bool isFromSelf;
/// 是否显示被跳转样式
final bool? isShowJump;
/// 清除跳转方法
final VoidCallback? clearJump;
/// 传入根据选定地图SDK实例化后的LocationUtils
final LocationUtils locationUtils;
/// 传入根据选定地图SDK实例化后的地图组件TIMMapWidget
final TIMMapWidget Function(VoidCallback onMapLoadDone, Key mapKey)
mapBuilder;
配合 TUIKit 使用接入
此部分完整代码可在 IM Flutter Demo 中得到。
历史消息列表渲染小地图(LocationMsgElement)
请在 TIMUIKitChat 中新增如下字段。该小地图可以自动单击跳转至完整版详细地图(LocationShow)。 TIMMapWidget 和 TIMMapService 需要替换为自己的实例化对象。
locationMessageItemBuilder:
(locationElem, isFromSelf, isShowJump, clearJump, messageID) =>
LocationMsgElement(
messageID: messageID,
locationElem: locationElem,
isFromSelf: isFromSelf,
isShowJump: isShowJump,
clearJump: clearJump,
mapBuilder: (onMapLoadDone, mapKey) => TIMMapWidget(
onMapLoadDone: onMapLoadDone,
key: mapKey,
),
locationUtils: LocationUtils(TIMMapService()),
),
加号面板增加位置 item 并跳转至位置选择器(LocationMsgElement)
请在 TIMUIKitChat 中新增如下字段。该 extraAction 可跳转至位置选择器,并发送消息。 TIMMapWidget 和 TIMMapService 需要替换为自己的实例化对象。
morePanelConfig: MorePanelConfig(
extraAction: [
MorePanelItem(
id: "location",
title: ("位置"),
onTap: (c) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LocationPicker(
onChange: (LocationMessage location) async {
// 此处消息发送逻辑需要根据业务框架适当修改
final locationMessageInfo = await sdkInstance.v2TIMMessageManager.createLocationMessage(
desc: location.desc, longitude: location.longitude, latitude: location.latitude);
final messageInfo = locationMessageInfo.data!.messageInfo;
_timuiKitChatController.sendMessage(
convID: _getConvID()!,
convType: _getConvType(),
messageInfo: messageInfo
);
},
mapBuilder: (onMapLoadDone, mapKey, onMapMoveEnd) => TIMMapWidget(
onMapMoveEnd: onMapMoveEnd,
onMapLoadDone: onMapLoadDone,
key: mapKey,
),
locationUtils: LocationUtils(TIMMapService()),
),
));
},
icon: Container(
height: 64,
width: 64,
margin: const EdgeInsets.only(bottom: 4),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Icon(
Icons.location_on,
color: hexToColor("5c6168"),
size: 32,
),
))
],
),