Flutter位置服务与UI组件插件tim_ui_kit_lbs_plugin的使用

Flutter位置服务与UI组件插件tim_ui_kit_lbs_plugin的使用



Tencent Chat Logo

腾讯云聊天位置消息

腾讯云聊天SDK的位置消息扩展。允许搜索并选择位置、发送位置和显示位置。

更多语言: 简体中文

腾讯云聊天Flutter UIKit LBS消息插件 #

通过此插件,开发者可以轻松地在项目中实现lbs(位置消息)。

我们提供了三种组件,包括:位置选择器(LocationPicker)/全页面位置显示(LocationShow)/历史消息列表中的位置显示(LocationMsgElement),除了与Map SDK交互的部分业务逻辑外,其他部分都参考这些组件。

此外,我们还提供了基于百度地图的复杂示例代码,以便于集成,可以直接运行,因此开发者可以根据我们的代码创建整个lbs项目。

词汇表 #

POI: 兴趣点,地图上的每个无地理意义的点。每个POI包含坐标、名称、地址和ID。 例如:白宫、悉尼大学等。

开发介绍 #

总体流程: #

  1. 选择特定的地图SDK
  2. 扩展三个抽象类,以连接此插件与您的地图SDK
  3. 实例化继承自抽象类的对象,并将其传递给插件提供的三个组件

选择特定的地图SDK #

地图SDK的选择不受限制,这里提供一些链接。

百度地图(中文):https://lbsyun.baidu.com/index.php?title=flutter/loc

谷歌地图:https://codelabs.developers.google.com/codelabs/google-maps-in-flutter#0

交互的数据结构 #

此插件定义了几种数据结构模型来描述传输的数据, 特别是在实现了抽象类与地图SDK之间的lbs业务功能时。 包括:

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信息列表。成员类型是TIMPoiInfo。
  List<TIMPoiInfo>? poiList;

  /// 当前POI位置的语义结果描述,用作地址名。
  /// 例如:"悉尼大学校区以南100米,位于坎培尔敦校区内"
  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,

  /// 由地图上的小部件事件触发,如点击切换地图类型
  Event,

  /// 由您的程序调用API接口事件触发,如重新设置地图参数导致地图区域变化。
  APIs,
}

/// 用于外部导航APP的应用信息。
class NavigationMapItem{
  /// 应用名称
  final String name;
  /// 跳转到导航APP的功能
  final Function(double longitude, double latitude) jumpFunc;

  NavigationMapItem(this.name, this.jumpFunc);
}

继承抽象类并与地图SDK连接插件业务功能 #

请根据您选择的地图SDK继承以下三个抽象类。

TIMMapService

获取当前位置并使用关键字和坐标搜索POI。 请使用您选择的地图SDK实现这些功能。

/// 【可选】如果需要使用地图SDK的定位能力,请实现此函数。开关:'isUseMapSDKLocation' of 'LocationPicker/LocationShow'。
/// 需要从地图SDK获取当前位置,然后通过'moveMapCenter'将地图中心移动到该位置。
/// 然后返回当前位置的坐标以及周围POI列表。
void moveToCurrentLocationActionWithSearchPOIByMapSDK({
  required void Function(TIMCoordinate coordinate) moveMapCenter,
  void Function(TIMReverseGeoCodeSearchResult, bool)?
  onGetReverseGeoCodeSearchResult,
});

/// 按关键字搜索POI列表,优先搜索指定城市的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

渲染所选地图的基本类。这是一个有状态的小部件,需要继承TIMMapState。 包括地图加载完成后和地图移动完成后的回调函数。

final Function? onMapLoadDone;
final Function(TIMCoordinate? targetGeoPt, TIMRegionChangeReason regionChangeReason)? onMapMoveEnd;

TIMMapState

渲染所选地图的基本类的状态。 包括几个与地图交互的函数,并返回可以直接使用的地图小部件。

/// 地图加载完成后的回调函数
void onMapLoadDone(){}

/// 地图移动后的回调函数
void onMapMoveEnd(TIMCoordinate? targetGeoPt, TIMRegionChangeReason regionChangeReason){}

/// 移动地图中心坐标
void moveMapCenter(TIMCoordinate pt){}

/// 禁止地图与手势等进行交互
void forbiddenMapFromInteract() {}

/// 在地图上特定坐标添加标记
void addMarkOnMap(TIMCoordinate pt, String title){}

/// 获取地图小部件
[@override](/user/override)
Widget build(BuildContext context) {
  return Container(
    child: 所选地图SDK的地图小部件(
      onMapCreated: onMapCreated,
    ),
  );
}

使用LBS组件 #

位置选择器(LocationPicker)

界面和功能类似于微信。 用户可以定位、选择地图上的点、搜索POI、显示周围POI列表。

/// 用户完成选择位置后的onChange(LocationMessage)回调。
/// 【提醒】此处的'LocationMessage.desc'将名称和地址拼接成一个字符串,因为腾讯云聊天的位置消息只包含一个字符串'desc'。
/// 例如:"悉尼大学/////Camperdown NSW 2006"。
/// 该拼接格式可以在本插件的任何地方解析。
final ValueChanged<LocationMessage> onChange;

/// TIMMapService,使用特定地图SDK实现的LocationUtils。
final LocationUtils locationUtils;

/// 定位前默认显示的中心坐标。
final TIMCoordinate? initCoordinate;

/// 控制是否需要使用地图SDK的定位能力,如果为true,请确保正确实现了'moveToCurrentLocationActionWithSearchPOIByMapSDK'。
final bool? isUseMapSDKLocation;

/// TIMMapWidget,使用由所选地图SDK继承的地图小部件。
final TIMMapWidget Function(
    VoidCallback onMapLoadDone,
    Key mapKey,
    Function(TIMCoordinate? targetGeoPt,
            TIMRegionChangeReason regionChangeReason)
        onMapMoveEnd) mapBuilder;

全页面位置显示(LocationShow)

界面和功能类似于微信。 在大地图中显示带位置标记的位置。位置名称和地址显示在底部,有一个跳转到地图APP导航的按钮。

/// 地址名称
final String addressName;

/// 完整地址
final String? addressLocation;

/// 纬度
final double latitude;

/// 经度
final double longitude;

/// TIMMapService,使用特定地图SDK实现的LocationUtils。
final LocationUtils locationUtils;

/// 控制是否需要使用地图SDK的定位能力,如果为true,请确保正确实现了'moveToCurrentLocationActionWithSearchPOIByMapSDK'。
final bool? isUseMapSDKLocation;

/// 外部导航地图列表,用于跳转导航,如果没有,将默认包含"Tencent Map"、"AMap"、"Baidu Map"、"Apple Map"。
final List<NavigationMapItem>? navigationMapList;

/// TIMMapWidget,使用由所选地图SDK继承的地图小部件。
final TIMMapWidget Function(
    VoidCallback onMapLoadDone,
    Key mapKey) mapBuilder;

历史消息列表中的位置显示(LocationMsgElement)

此组件用于在历史消息列表中以较小的框显示LBS消息。 包含位置名称、完整地址和一个小地图。

/// 消息ID
final String? messageID;

/// V2TimLocationElem消息
final V2TimLocationElem locationElem;

/// 是否此消息来自自己
final bool isFromSelf;

/// 是否显示代表此消息已被跳转的样式
final bool? isShowJump;

/// 清除跳转功能(通常与UIKit一起使用)
final VoidCallback? clearJump;

/// TIMMapService,使用特定地图SDK实现的LocationUtils。
final LocationUtils locationUtils;

/// 控制是否需要使用地图SDK的定位能力,如果为true,请确保正确实现了'moveToCurrentLocationActionWithSearchPOIByMapSDK'。
final bool? isUseMapSDKLocation;

/// TIMMapWidget,使用由所选地图SDK继承的地图小部件。
final TIMMapWidget Function(VoidCallback onMapLoadDone, Key mapKey)
    mapBuilder;

与TIM Flutter TUIKit集成 #

虽然这部分代码是可选的,意味着如果您倾向于自行实现集成,可以在任何需要的地方使用上述组件, 但您仍然可以阅读这部分,以了解如何调用这些组件。

在历史消息列表中渲染消息(LocationMsgElement)

请在'TIMUIKitChat'中添加以下参数。它将在历史消息列表中显示一个小框。 通过单击此项目,它将直接跳转到'LocationShow',因此在这种情况下无需在程序内部调用'LocationShow'。

'TIMMapWidget'和'TIMMapService'需要替换为您使用特定地图SDK实现的继承类,如示例所示。

messageItemBuilder: MessageItemBuilder(
  locationMessageItemBuilder: (message, isShowJump, clearJump) {
    return LocationMsgElement(
      messageID: message.msgID,
      locationElem: LocationMessage(),
      isFromSelf: message.isSelf ?? false,
      isShowJump: isShowJump,
      clearJump: clearJump,
      mapBuilder: (onMapLoadDone, mapKey) => TIMMapWidget(
        onMapLoadDone: onMapLoadDone,
        key: mapKey,
      ),
      locationUtils: LocationUtils(TIMMapService()),
    );
  },
),

在更多面板中添加“位置”按钮以调用位置选择器(LocationPicker)

请在'TIMUIKitChat'中添加以下参数。 此extraAction可以跳转到'LocationPicker'并发送LBS消息。

'TIMMapWidget'和'TIMMapService'需要替换为您使用特定地图SDK实现的继承类,如示例所示。

morePanelConfig: MorePanelConfig(
  extraAction: [
    MorePanelItem(
        id: "location",
        title: ("位置"),
        onTap: (c) {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => LocationPicker(
                onChange: (LocationMessage location) async {
                  // 发送位置消息的业务逻辑需要根据您的程序进行修改。
                  // 如果配合UIKit使用,请参考插件README。
                  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,
          ),
        )
    )
  ],
),

联系我们 #

如果您有任何进一步的问题或希望了解更多用例,请随时联系我们。

  • Telegram群组:https://t.me/+1doS9AUBmndhNGNl
  • WhatsApp群组:https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A
  • QQ群:788910197,中文聊天

我们的网站:https://www.tencentcloud.com/products/im?from=pub


示例代码

import 'package:flutter/material.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
import 'package:flutter_baidu_mapapi_search/flutter_baidu_mapapi_search.dart';
import 'package:flutter_bmflocation/flutter_bmflocation.dart';
import 'package:tim_ui_kit_lbs_plugin/abstract/map_class.dart';
import 'package:tim_ui_kit_lbs_plugin/abstract/map_service.dart';
import 'package:tim_ui_kit_lbs_plugin/abstract/map_widget.dart';
import 'package:tim_ui_kit_lbs_plugin/utils/location_utils.dart';
import 'package:tim_ui_kit_lbs_plugin/utils/tim_location_model.dart';
import 'package:tim_ui_kit_lbs_plugin/utils/utils.dart';
import 'package:tim_ui_kit_lbs_plugin/pages/location_picker.dart';
import 'package:tim_ui_kit_lbs_plugin/pages/location_show.dart';
import 'package:tim_ui_kit_lbs_plugin/widget/location_msg_element.dart';
import 'dart:io' show Platform;

// 本Sample以百度地图为底层SDK演示如何使用腾讯云IM Flutter LBS能力插件
// 有关如何快速集成本插件与百度地图Flutter SDK,请参考此文档:https://docs.qq.com/doc/DSXRBZG1CUGhIVEZx

// 本Sample主要介绍如何根据选定地图SDK继承tim_ui_kit_lbs_plugin的三个抽象类
// 分别为:TIMMapService/TIMMapWidget/TIMMapState

// 并演示如何将实例化后的三个类,传入LBS组件中,实现三个功能组件
// 分别为:位置选择器(LocationPicker)/位置消息完整展示器(LocationShow)/位置消息列表展示器(LocationMsgElement)

void main() {
  runApp(const MaterialApp(
    home: Home(),
  ));
}

class Home extends StatefulWidget {
  const Home({Key? key}) : super(key: key);

  [@override](/user/override)
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  // 请自行修改index查看不同组件用法。
  // 0: 位置选择器  1:位置消息完整展示器  2: 位置消息列表展示器
  int currentDemoPageIndex = 0;

  [@override](/user/override)
  void initState() {
    super.initState();
  }

  [@override](/user/override)
  void dispose() {
    super.dispose();
  }

  List<Widget> demoPageList() => [
        // 0: 位置选择器  1:位置消息完整展示器  2: 位置消息列表展示器
        LocationPicker(
          onChange: (LocationMessage location) async {
            // 发送位置消息的业务逻辑需要根据您的程序进行修改。
            // 如果配合UIKit使用,请参考插件README
            // https://pub.dev/packages/tim_ui_kit_lbs_plugin#uikit
          },
          mapBuilder: (onMapLoadDone, mapKey, onMapMoveEnd) => BaiduMap(
            onMapMoveEnd: onMapMoveEnd,
            onMapLoadDone: onMapLoadDone,
            key: mapKey,
          ),
          locationUtils: LocationUtils(BaiduMapService()),
        ),
        LocationShow(
          addressName: "腾讯大厦",
          addressLocation: "广东省深圳市深南大道10000号",
          longitude: 22.546103,
          latitude: 113.941079,
          mapBuilder: (onMapLoadDone, mapKey) => BaiduMap(
            onMapLoadDone: onMapLoadDone,
            key: mapKey,
          ),
          locationUtils: LocationUtils(BaiduMapService()),
        ),
        LocationMsgElement(
          messageID: "0312",
          locationElem: LocationMessage(
              latitude: 113.941079,
              longitude: 22.546103,
              desc: "腾讯大厦/////广东省深圳市深南大道10000号"),
          isFromSelf: true,
          isShowJump: false,
          clearJump: () {},
          mapBuilder: (onMapLoadDone, mapKey) => BaiduMap(
            onMapLoadDone: onMapLoadDone,
            key: mapKey,
          ),
          locationUtils: LocationUtils(BaiduMapService()),
        ),
      ];

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: Text('腾讯云IM Flutter LBS'),
          ),
          body: Center(
            child: demoPageList()[currentDemoPageIndex],
          )),
    );
  }
}

// 使用百度地图继承TIMMapService的sample
class BaiduMapService extends TIMMapService {

  // 请前往百度地图开放平台申请iOS端AK,用于定位
  // https://lbsyun.baidu.com/apiconsole/key#/home
  String appKey = "";

  // 使用百度地图提供的定位能力,需要先安装flutter_bmflocation包。
  [@override](/user/override)
  void moveToCurrentLocationActionWithSearchPOIByMapSDK({
    required void Function(TIMCoordinate coordinate) moveMapCenter,
    void Function(TIMReverseGeoCodeSearchResult, bool)?
    onGetReverseGeoCodeSearchResult,
  }) async {
    await initBaiduLocationPermission();
    Map iosMap = initIOSOptions().getMap();
    Map androidMap = initAndroidOptions().getMap();
    final LocationFlutterPlugin _myLocPlugin = LocationFlutterPlugin();

    // 根据定位数据挪地图及加载周边POI列表
    void dealWithLocationResult(BaiduLocation result) {
      if (result.latitude != null && result.longitude != null) {
        TIMCoordinate coordinate =
        TIMCoordinate(result.latitude!, result.longitude!);
        moveMapCenter(coordinate);
        if(onGetReverseGeoCodeSearchResult != null){
          searchPOIByCoordinate(
              coordinate: coordinate,
              onGetReverseGeoCodeSearchResult: onGetReverseGeoCodeSearchResult);
        }
      } else {
        Utils.toast(("定位失败"));
      }
    }

    // 设置获取到定位后的回调
    if (Platform.isIOS) {
      _myLocPlugin.singleLocationCallback(callback: (BaiduLocation result) {
        dealWithLocationResult(result);
      });
    } else if (Platform.isAndroid) {
      _myLocPlugin.seriesLocationCallback(callback: (BaiduLocation result) {
        dealWithLocationResult(result);
        _myLocPlugin.stopLocation();
      });
    }

    // 启动定位
    await _myLocPlugin.prepareLoc(androidMap, iosMap);
    if (Platform.isIOS) {
      _myLocPlugin
          .singleLocation({'isReGeocode': true, 'isNetworkState': true});
    } else if (Platform.isAndroid) {
      _myLocPlugin.startLocation();
    }
  }

  static BaiduLocationAndroidOption initAndroidOptions() {
    BaiduLocationAndroidOption options = BaiduLocationAndroidOption(
        coorType: 'bd09ll',
        locationMode: BMFLocationMode.hightAccuracy,
        isNeedAddress: true,
        isNeedAltitude: true,
        isNeedLocationPoiList: true,
        isNeedNewVersionRgc: true,
        isNeedLocationDescribe: true,
        openGps: true,
        locationPurpose: BMFLocationPurpose.sport,
        coordType: BMFLocationCoordType.bd09ll);
    return options;
  }

  static BaiduLocationIOSOption initIOSOptions() {
    BaiduLocationIOSOption options = BaiduLocationIOSOption(
        coordType: BMFLocationCoordType.bd09ll,
        BMKLocationCoordinateType: 'BMKLocationCoordinateTypeBMK09LL',
        desiredAccuracy: BMFDesiredAccuracy.best);
    return options;
  }

  initBaiduLocationPermission() async {
    LocationFlutterPlugin myLocPlugin = LocationFlutterPlugin();
    // 动态申请定位权限
    await LocationUtils.requestLocationPermission();
    // 设置是否隐私政策
    myLocPlugin.setAgreePrivacy(true);
    BMFMapSDK.setAgreePrivacy(true);
    if (Platform.isIOS) {
      // 设置ios端ak, Android端ak可以直接在清单文件中配置
      myLocPlugin.authAK(appKey);
    }
  }

  [@override](/user/override)
  void poiCitySearch({
    required void Function(List<TIMPoiInfo>?, bool)
    onGetPoiCitySearchResult,
    required String keyword,
    required String city,
  }) async {
    BMFPoiCitySearchOption citySearchOption = BMFPoiCitySearchOption(
      city: city,
      keyword: keyword,
      scope: BMFPoiSearchScopeType.DETAIL_INFORMATION,
      isCityLimit: false,
    );

    BMFPoiCitySearch citySearch = BMFPoiCitySearch();

    // 检索回调
    citySearch.onGetPoiCitySearchResult(
        callback: (result, errorCode) {
          List<TIMPoiInfo> tmpPoiInfoList = [];
          result.poiInfoList?.forEach((v) {
            tmpPoiInfoList.add(TIMPoiInfo.fromMap(v.toMap()));
          });
          onGetPoiCitySearchResult(
              tmpPoiInfoList,
              errorCode != BMFSearchErrorCode.NO_ERROR
          );
        }
    );

    // 发起检索
    bool result = await citySearch.poiCitySearch(citySearchOption);

    if (result) {
      print(("检索成功"));
    } else {
      print(("检索失败"));
    }
  }

  [@override](/user/override)
  void searchPOIByCoordinate(
      {required TIMCoordinate coordinate,
        required void Function(TIMReverseGeoCodeSearchResult, bool)
        onGetReverseGeoCodeSearchResult}) async {
    BMFReverseGeoCodeSearchOption option = BMFReverseGeoCodeSearchOption(
      location: BMFCoordinate.fromMap(coordinate.toMap()),
    );

    BMFReverseGeoCodeSearch reverseGeoCodeSearch = BMFReverseGeoCodeSearch();

    // 注册检索回调
    reverseGeoCodeSearch.onGetReverseGeoCodeSearchResult(
        callback: (result, errorCode){
          print("失败原因 ${errorCode} ${errorCode.name} ${errorCode.toString()}");
          return onGetReverseGeoCodeSearchResult(
              TIMReverseGeoCodeSearchResult.fromMap(result.toMap()),
              errorCode != BMFSearchErrorCode.NO_ERROR
          );
        });

    // 发起检索
    bool result = await reverseGeoCodeSearch.reverseGeoCodeSearch(BMFReverseGeoCodeSearchOption.fromMap(option.toMap()));

    if (result) {
      print(("检索成功"));
    } else {
      print(("检索失败"));
    }
  }

}

// 使用百度地图继承TIMMapWidget的sample
class BaiduMap extends TIMMapWidget {
  final Function? onMapLoadDone;
  final Function(TIMCoordinate? targetGeoPt, TIMRegionChangeReason regionChangeReason)? onMapMoveEnd;

  const BaiduMap({Key? key, this.onMapLoadDone, this.onMapMoveEnd}) : super(key: key);

  [@override](/user/override)
  State<StatefulWidget> createState() => BaiduMapState();
}

// 使用百度地图继承TIMMapState的sample
class BaiduMapState extends TIMMapState<BaiduMap> {
  late BMFMapController timMapController;
  Widget mapWidget = Container();

  // 创建完成回调
  void onMapCreated(BMFMapController controller) {
    timMapController = controller;

    timMapController.setMapDidLoadCallback(callback: () {
      print(('mapDidLoad-地图加载完成'));
    });

    // 设置移动结束回调
    timMapController.setMapRegionDidChangeWithReasonCallback(callback: (status, reason) => onMapMoveEnd(
        status.targetGeoPt != null ? TIMCoordinate.fromMap(status.targetGeoPt!.toMap()) : null,
        TIMRegionChangeReason.values[reason.index]),
    );

    if(widget.onMapLoadDone != null){
      widget.onMapLoadDone!();
    }
  }

  // 地图移动结束
  [@override](/user/override)
  void onMapMoveEnd(TIMCoordinate? targetGeoPt, TIMRegionChangeReason regionChangeReason){
    if(widget.onMapMoveEnd != null){
      widget.onMapMoveEnd!(targetGeoPt, regionChangeReason);
    }
  }

  // 移动地图视角
  [@override](/user/override)
  void moveMapCenter(TIMCoordinate pt){
    timMapController.setCenterCoordinate(BMFCoordinate.fromMap(pt.toMap()), true, animateDurationMs: 1000);
  }

  [@override](/user/override)
  void forbiddenMapFromInteract() {
    timMapController.updateMapOptions(BMFMapOptions(
      scrollEnabled: false,
      zoomEnabled: false,
      overlookEnabled: false,
      rotateEnabled: false,
      gesturesEnabled: false,
      changeCenterWithDoubleTouchPointEnabled: false,
    ));
  }

  [@override](/user/override)
  void addMarkOnMap(TIMCoordinate pt, String title){
    BMFMarker marker = BMFMarker.icon(
        position: BMFCoordinate.fromMap(pt.toMap()),
        title: title,
        identifier: 'flutter_marker',
        icon: 'assets/pin_red.png');

    timMapController.addMarker(marker);
  }

  // 设置地图参数
  BMFMapOptions initMapOptions() {
    BMFMapOptions mapOptions = BMFMapOptions(
      center: BMFCoordinate(39.917215, 116.380341),
      zoomLevel: 18,
    );
    return mapOptions;
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      child: BMFMapWidget(
        onBMFMapCreated: onMapCreated,
        mapOptions: initMapOptions(),
      ),
    );
  }
}

更多关于Flutter位置服务与UI组件插件tim_ui_kit_lbs_plugin的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter位置服务与UI组件插件tim_ui_kit_lbs_plugin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter应用中使用位置服务插件(如geolocation插件)以及自定义UI组件插件(假设tim_ui_kit_lbs_plugin提供了类似地图显示和位置标记的功能)的示例代码。由于tim_ui_kit_lbs_plugin可能是一个假想的或特定环境下的插件,这里我会提供一个基于通用概念的实现,并假设该插件具有类似功能。

首先,确保你已经在pubspec.yaml文件中添加了必要的依赖项:

dependencies:
  flutter:
    sdk: flutter
  geolocator: ^9.0.2  # 假设使用geolocator插件进行位置获取
  tim_ui_kit_lbs_plugin: # 添加实际的插件版本,如果可用
    git:
      url: 'https://github.com/your-repo/tim_ui_kit_lbs_plugin.git'  # 假设这是一个git仓库
      ref: 'main'

然后,运行flutter pub get来安装这些依赖。

接下来,在你的Flutter应用中实现位置服务和UI组件的使用:

import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:geolocator_platform_interface/geolocator_platform_interface.dart';
import 'package:tim_ui_kit_lbs_plugin/tim_ui_kit_lbs_plugin.dart';  // 假设插件提供了这样的导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Location Service Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Position? _currentPosition;

  @override
  void initState() {
    super.initState();
    _getCurrentLocation();
  }

  Future<void> _getCurrentLocation() async {
    bool serviceEnabled;
    LocationPermission permission;

    // Test if location services are enabled.
    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return Future.error('Location services are disabled.');
    }

    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        return Future.error('Location permissions are denied');
      }
    }

    if (permission == LocationPermission.deniedForever) {
      // Permissions are permanently denied, we cannot request permissions anymore.
      return Future.error(
          'Location permissions are permanently denied, we cannot request permissions.');
    }

    _currentPosition = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high);

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Location Service Demo'),
      ),
      body: _currentPosition == null
          ? Center(
              child: CircularProgressIndicator(),
            )
          : Center(
              child: TimLbsMapWidget(
                initialPosition: _currentPosition!,
                onMapCreated: (mapController) {
                  // 在地图创建完成后可以执行的操作,比如添加标记等
                  // 假设TimLbsMapWidget提供了这样的回调
                },
              ),
            ),
    );
  }
}

// 假设TimLbsMapWidget是tim_ui_kit_lbs_plugin提供的组件
class TimLbsMapWidget extends StatefulWidget {
  final Position initialPosition;
  final Function(dynamic)? onMapCreated;

  const TimLbsMapWidget({
    Key? key,
    required this.initialPosition,
    this.onMapCreated,
  }) : super(key: key);

  @override
  _TimLbsMapWidgetState createState() => _TimLbsMapWidgetState();
}

class _TimLbsMapWidgetState extends State<TimLbsMapWidget> {
  // 假设插件提供了地图控制器或其他需要的状态管理
  @override
  Widget build(BuildContext context) {
    // 这里应该使用插件提供的具体组件和API来构建UI
    // 由于tim_ui_kit_lbs_plugin是假设的,以下代码仅为示例
    return Container(
      child: CustomPaint(
        // 使用CustomPaint仅作为占位符,实际应使用插件提供的地图组件
        painter: _MapPainter(position: widget.initialPosition),
      ),
    );
  }
}

class _MapPainter extends CustomPainter {
  final Position position;

  _MapPainter({required this.position});

  @override
  void paint(Canvas canvas, Size size) {
    // 这里应该绘制地图和位置标记,但由于是示例,仅绘制一个圆点表示位置
    final paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.fill;
    final center = Offset(size.width / 2, size.height / 2);
    // 注意:这里的位置转换是简化的,实际应使用地图的坐标转换API
    canvas.drawCircle(center, 10, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false; // 简化示例,不重新绘制
  }
}

注意

  1. 上面的代码假设tim_ui_kit_lbs_plugin提供了类似TimLbsMapWidget的组件,以及地图创建后的回调功能。实际使用时,你需要根据插件的文档来调整代码。
  2. geolocator插件用于获取当前位置,你需要根据geolocator的文档处理位置权限和位置获取。
  3. _MapPainter仅作为占位符,用于展示如何在没有具体插件组件的情况下绘制一个简单的位置标记。实际应使用插件提供的地图组件和API。

确保你查阅tim_ui_kit_lbs_plugin的官方文档,以获取正确的组件使用方法和API调用方式。

回到顶部