flutter 最近有在地图上选择地址的需求,要求如下
1.移动地图获取根据地图中心点获取周边的poi信息
2.搜索,根据搜索内容提示相关地点信息,点击移动到相关位置,显示出该位置周边的poi信息
废话少说,先上视频
flutter百度地图选址
百度地图的集成这个就不说了,直接参考官网集成即可,咱们直接看实现代码
先看布局
布局比较简单,就是上面两个按钮,下面是固定高度的搜索框和列表,有一些我封装的内容,大家可以自行绘制,主要看与地图相关的内容即可
return Column(
      children: [
        Expanded(
            child: Stack(
          children: [
            BMFMapWidget(
              onBMFMapCreated: (controller) {
                viewModel.mapController = controller;
                onBMFMapCreated();
              },
              mapOptions: viewModel.mapOptions,
            ),
            Container(
              padding: const EdgeInsets.fromLTRB(10, 10, 10, 20),
              decoration: const BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [
                    Color(0x80a6a6a6),
                    //white,
                    transparent,
                  ],
                ),
              ),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  TextView(
                    "取消",
                    textSize: 15,
                    textColor: Colors.white,
                    paddingLeft: 10,
                    paddingRight: 10,
                    paddingTop: 5,
                    paddingBottom: 5,
                    onClick: () {
                      Get.back();
                    },
                  ),
                  TextView(
                    "确定",
                    backgroundColor: green,
                    cornerRadius: 2,
                    textSize: 15,
                    textColor: Colors.white,
                    paddingLeft: 10,
                    paddingTop: 5,
                    paddingBottom: 5,
                    paddingRight: 10,
                    onClick: () {
                      if (viewModel.poiChooseName.isEmpty) {
                        showToast("您还未选择任何地址");
                      }  else{
                        Get.back(result: viewModel.poiInfo);
                      }
                    },
                  )
                ],
              ),
            ),
            Center(
                child: ImageView(
              "assets/images/ic_map_location.png",
              width: 30,
              height: 30,
            )),
          ],
        )),
        MyContainer(
          height: viewModel.contentHieght,
          backgroundColor: Colors.white,
          cornerRadius: 10,
          child: Column(
            children: [
              const SizedBox(
                height: 10,
              ),
              CommonSearchLayout(viewModel.searchController),
              Expanded(
                child: MyListView(
                    viewModel.isSearch
                        ? viewModel.suggestList
                        : viewModel.poiList,
                    dividerHeight: 1,
                    dividerColor: dividerColor, (context, index) {
                  if (viewModel.isSearch) {
                    //搜索
                    return SuggestItem(viewModel.suggestList[index], index,
                        (index1) {
                      onSuggestChoose(index1);
                    });
                  } else {
                    //选择
                    return PoiItem(
                        viewModel.poiList[index], viewModel.poiChooseName,
                        (text) {
                      viewModel.poiChooseName = text;
                      viewModel.poiInfo = viewModel.poiList[index];
                      notifyChanges();
                    });
                  }
                }),
              )
            ],
          ),
        )
      ],
    );展现出来的样式

注意地图创建后调用的发方法onBMFMapCreated(),当地图创建之后,判断地图是否确实是被加载好了,加载好了之后就调用一个获取用户位置的方法,把用户当前定位挪到地图中心去,然后设置地图监听,也就是当地区的中心区域有变化的时候,重新获取地图中心点周边的poi信息
void onBMFMapCreated() {
    //地图被创建
    viewModel.mapController.setMapDidLoadCallback(callback: () {
      //地图已加载,地图中心移动到用户所在的位置
      showUserLocation();
    });
    //监听地图区域变化
    viewModel.mapController.setMapRegionDidChangeWithReasonCallback(callback:
        (BMFMapStatus mapStatus, BMFRegionChangeReason regionChangeReason) {
      print("地图区域变化:${mapStatus.targetGeoPt!.latitude}");
      viewModel.poiChooseName = "";
      //根据地图中心点检索周边事物
      getPoiByLocation(
          mapStatus.targetGeoPt!.latitude, mapStatus.targetGeoPt!.longitude);
      notifyChanges();
    });
  }获取用户位置并且把用户位置移动到中央,LocationUtils().getCurrentLocation()是我自己封装的获取用户当前位置的方法,调用的是百度地图官方的sdk的方法,自己封装了一下,这块大家可以自己来写
//获取用户位置并移动地图到人员中心
  void showUserLocation() {
    LocationUtils().getCurrentLocation(context, (info) {
      viewModel.mapOptions = BMFMapOptions(
        center: BMFCoordinate(info.latitude!, info.longitude!),
        zoomLevel: viewModel.mapLevel,
      );
      viewModel.mapController.updateMapOptions(viewModel.mapOptions);
      getPoiByLocation(info.latitude!, info.longitude!);
    });
  }然后重点来了getPoiByLocation(),这个方法很重要,这个方法是干什么的呢,这个方法就是根据坐标获取周边poi信息的方法,本质上就是坐标转地址(反地理编码),他会给出很多这个坐标周边的poi信息,这个周边的poi信息就是咱们最关键最需要的信息
这里面相应的位置点的名称、地址、坐标什么的就都获取到了,List<BMFPoiInfo>? list,这个就是获取到的poi信息的列表,里面我做了一些小处理,根据搜索的内容把转化的poi信息与之相符的放在第一位,这个列表获取到了,后续怎么处理根据需求自行处理即可
//反地理编码,根据坐标查询poi信息
void getPoiByLocation(double latitude, double longitude) async {
    BMFReverseGeoCodeSearchOption reverseGeoCodeSearchOption =
        BMFReverseGeoCodeSearchOption(
            location: BMFCoordinate(latitude, longitude));
    // 检索实例
    BMFReverseGeoCodeSearch reverseGeoCodeSearch = BMFReverseGeoCodeSearch();
    // 逆地理编码回调
    reverseGeoCodeSearch.onGetReverseGeoCodeSearchResult(callback:
        (BMFReverseGeoCodeSearchResult result, BMFSearchErrorCode errorCode) {
      List<BMFPoiInfo>? list = result.poiList;
      viewModel.poiList.clear();
      if (result.poiList!.isNotEmpty) {
        viewModel.poiList.addAll(result.poiList!);
        if (viewModel.poiChooseName.isNotEmpty) {
          late BMFPoiInfo info;
          for (int i = 0; i < viewModel.poiList.length; i++) {
            if (viewModel.poiList[i].name == viewModel.poiChooseName) {
              viewModel.poiInfo = viewModel.poiList[i];
              viewModel.poiList.removeAt(i);
              viewModel.poiList.insert(0, viewModel.poiInfo);
            }
          }
        }
        notifyChanges();
        for (int i = 0; i < list!.length; i++) {
          BMFPoiInfo info = list[i];
          print("poi信息:${info.name!}---${info.address!}");
          print("poi信息:${info.pt!.longitude}---${info.pt!.latitude}");
        }
      } else {
        print("poi信息:空");
      }
      //print(`逆地理编码  errorCode = ${errorCode}  \n result = ${result?.toMap()}`);
      // 解析reslut,具体参考demo
    });
    await reverseGeoCodeSearch.reverseGeoCodeSearch(reverseGeoCodeSearchOption);
  }再然后就是搜索了,因为我搜索和现实位置的时候用的是同一个listview,所以我加了一个状态的判断
//当前是否是搜索的状态
  bool isSearch = false;以此来判断当前是显示poi信息,还是显示的搜索结果,监听输入框,当输入文字有变化的时候,搜索内容,关键方法searchLocation(),cityname给一个就行,不给会报错,是可以进行全国范围内搜索的,这个点不要怕
//根据输入的内容搜索相应的位置
  void searchLocation() async {
    viewModel.suggestList.clear();
    BMFSuggestionSearchOption suggestionSearchOption =
        BMFSuggestionSearchOption(
            keyword: viewModel.searchController.text, cityname: '北京市');
    BMFSuggestionSearch suggestionSearch = BMFSuggestionSearch();
    suggestionSearch.onGetSuggestSearchResult(callback:
        (BMFSuggestionSearchResult result, BMFSearchErrorCode errorCode) {
      if (result.suggestionList!.isNotEmpty) {
        //有搜索结果
        viewModel.suggestList.addAll(result.suggestionList!);
        for (int i = 0; i < result.suggestionList!.length; i++) {
          print("提醒位置:${result.suggestionList![i].key}");
        }
      } else {
        print("提醒位置:空");
      }
      notifyChanges();
    });
    await suggestionSearch.suggestionSearch(suggestionSearchOption);
  }当选择了搜索的位置之后,就需要把地图移动到相应的位置,然后重新获取次位置周边的poi信息
,主要方法onSuggestChoose(),当搜索提示的位置选择之后需要做的处理
void onSuggestChoose(int index) {
    //选中此项
    viewModel.suggestInfo = viewModel.suggestList[index];
    viewModel.poiChooseName = viewModel.suggestInfo.key!;
    //位置移动到此处
    viewModel.mapOptions = BMFMapOptions(
      center: BMFCoordinate(viewModel.suggestInfo.location!.latitude,
          viewModel.suggestInfo.location!.longitude),
      //zoomLevel: viewModel.mapLevel,
    );
    viewModel.mapController.updateMapOptions(viewModel.mapOptions);
    //关闭键盘
    SystemChannels.textInput.invokeMethod('TextInput.hide');
    //搜索周边poi
    getPoiByLocation(viewModel.suggestInfo.location!.latitude,
        viewModel.suggestInfo.location!.longitude);
    //搜索状态改为选择状态
    viewModel.isSearch = false;
    notifyChanges();
  }核心代码基本上就是这样,其中一些组件是我封装的,无非也就是一些textfield,text,listview之类的,整体是封装了state,使用的是get_it这个库进行状态管理
下面我把完整的代码贴上来,大家参考一下
主要页面,大家的布局直接放在build里面就行,我外面进行了一次封装
import 'package:flutter/material.dart';
import 'package:flutter/services.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_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:get/get.dart';
import 'package:status_bar_control/status_bar_control.dart';
import 'package:zuguantong/activity/map/choose/item_poi.dart';
import 'package:zuguantong/activity/map/choose/item_suggest.dart';
import 'package:zuguantong/activity/map/choose/mapchoose_view_model.dart';
import 'package:zuguantong/yuchuangbase/const/colors.dart';
import 'package:zuguantong/yuchuangbase/utils/location_utils.dart';
import 'package:zuguantong/yuchuangbase/widget/ImageView.dart';
import 'package:zuguantong/yuchuangbase/widget/MyContainer.dart';
import 'package:zuguantong/yuchuangbase/widget/MyListView.dart';
import 'package:zuguantong/yuchuangbase/widget/TextView.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import '../../../yuchuangbase/base/base_state.dart';
import '../../../yuchuangbase/widget/CommonSearchLayout.dart';
///地图选址
class MapChooseActivity extends StatefulWidget {
  var keyboardVisibilityController = KeyboardVisibilityController();
  MapChooseActivity({Key? key}) : super(key: key);
  @override
  State<MapChooseActivity> createState() => _MapChooseActivityState();
}
class _MapChooseActivityState
    extends BaseState<MapChooseActivity, MapChooseViewModel> {
  @override
  void onCreate() async {
    hideTitle();
    showContent();
    viewModel.searchController.addListener(() {
      print("内容变化:开始");
      if (viewModel.searchController.text.isEmpty) {
        viewModel.isSearch = false;
        notifyChanges();
      }  else{
        viewModel.isSearch = true;
        searchLocation();
      }
    });
    //await StatusBarControl.setHidden(true, animation:StatusBarAnimation.NONE);
    widget.keyboardVisibilityController.onChange.listen((bool visible) {
      print('键盘: $visible');
      if (visible) {
        viewModel.isSearch = true;
      } else {
        viewModel.isSearch = false;
      }
      notifyChanges();
    });
  }
  @override
  void dispose() {
    super.dispose();
    SystemChannels.textInput.invokeMethod('TextInput.hide');
  }
  @override
  getContent(BuildContext context) {
    return Column(
      children: [
        Expanded(
            child: Stack(
          children: [
            BMFMapWidget(
              onBMFMapCreated: (controller) {
                viewModel.mapController = controller;
                onBMFMapCreated();
              },
              mapOptions: viewModel.mapOptions,
            ),
            Container(
              padding: const EdgeInsets.fromLTRB(10, 10, 10, 20),
              decoration: const BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [
                    Color(0x80a6a6a6),
                    //white,
                    transparent,
                  ],
                ),
              ),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  TextView(
                    "取消",
                    textSize: 15,
                    textColor: Colors.white,
                    paddingLeft: 10,
                    paddingRight: 10,
                    paddingTop: 5,
                    paddingBottom: 5,
                    onClick: () {
                      Get.back();
                    },
                  ),
                  TextView(
                    "确定",
                    backgroundColor: green,
                    cornerRadius: 2,
                    textSize: 15,
                    textColor: Colors.white,
                    paddingLeft: 10,
                    paddingTop: 5,
                    paddingBottom: 5,
                    paddingRight: 10,
                    onClick: () {
                      if (viewModel.poiChooseName.isEmpty) {
                        showToast("您还未选择任何地址");
                      }  else{
                        Get.back(result: viewModel.poiInfo);
                      }
                    },
                  )
                ],
              ),
            ),
            Center(
                child: ImageView(
              "assets/images/ic_map_location.png",
              width: 30,
              height: 30,
            )),
          ],
        )),
        MyContainer(
          height: viewModel.contentHieght,
          backgroundColor: Colors.white,
          cornerRadius: 10,
          child: Column(
            children: [
              const SizedBox(
                height: 10,
              ),
              CommonSearchLayout(viewModel.searchController),
              Expanded(
                child: MyListView(
                    viewModel.isSearch
                        ? viewModel.suggestList
                        : viewModel.poiList,
                    dividerHeight: 1,
                    dividerColor: dividerColor, (context, index) {
                  if (viewModel.isSearch) {
                    //搜索
                    return SuggestItem(viewModel.suggestList[index], index,
                        (index1) {
                      onSuggestChoose(index1);
                    });
                  } else {
                    //选择
                    return PoiItem(
                        viewModel.poiList[index], viewModel.poiChooseName,
                        (text) {
                      viewModel.poiChooseName = text;
                      viewModel.poiInfo = viewModel.poiList[index];
                      notifyChanges();
                    });
                  }
                }),
              )
            ],
          ),
        )
      ],
    );
  }
  void onBMFMapCreated() {
    //地图被创建
    viewModel.mapController.setMapDidLoadCallback(callback: () {
      //地图已加载,地图中心移动到用户所在的位置
      showUserLocation();
    });
    //监听地图区域变化
    viewModel.mapController.setMapRegionDidChangeWithReasonCallback(callback:
        (BMFMapStatus mapStatus, BMFRegionChangeReason regionChangeReason) {
      print("地图区域变化:${mapStatus.targetGeoPt!.latitude}");
      viewModel.poiChooseName = "";
      //根据地图中心点检索周边事物
      getPoiByLocation(
          mapStatus.targetGeoPt!.latitude, mapStatus.targetGeoPt!.longitude);
      notifyChanges();
    });
  }
  //获取用户位置并移动地图到人员中心
  void showUserLocation() {
    LocationUtils().getCurrentLocation(context, (info) {
      viewModel.mapOptions = BMFMapOptions(
        center: BMFCoordinate(info.latitude!, info.longitude!),
        zoomLevel: viewModel.mapLevel,
      );
      viewModel.mapController.updateMapOptions(viewModel.mapOptions);
      getPoiByLocation(info.latitude!, info.longitude!);
    });
  }
  void getPoiByLocation(double latitude, double longitude) async {
    BMFReverseGeoCodeSearchOption reverseGeoCodeSearchOption =
        BMFReverseGeoCodeSearchOption(
            location: BMFCoordinate(latitude, longitude));
    // 检索实例
    BMFReverseGeoCodeSearch reverseGeoCodeSearch = BMFReverseGeoCodeSearch();
    // 逆地理编码回调
    reverseGeoCodeSearch.onGetReverseGeoCodeSearchResult(callback:
        (BMFReverseGeoCodeSearchResult result, BMFSearchErrorCode errorCode) {
      List<BMFPoiInfo>? list = result.poiList;
      viewModel.poiList.clear();
      if (result.poiList!.isNotEmpty) {
        viewModel.poiList.addAll(result.poiList!);
        if (viewModel.poiChooseName.isNotEmpty) {
          late BMFPoiInfo info;
          for (int i = 0; i < viewModel.poiList.length; i++) {
            if (viewModel.poiList[i].name == viewModel.poiChooseName) {
              viewModel.poiInfo = viewModel.poiList[i];
              viewModel.poiList.removeAt(i);
              viewModel.poiList.insert(0, viewModel.poiInfo);
            }
          }
        }
        notifyChanges();
        for (int i = 0; i < list!.length; i++) {
          BMFPoiInfo info = list[i];
          print("poi信息:${info.name!}---${info.address!}");
          print("poi信息:${info.pt!.longitude}---${info.pt!.latitude}");
        }
      } else {
        print("poi信息:空");
      }
      //print(`逆地理编码  errorCode = ${errorCode}  \n result = ${result?.toMap()}`);
      // 解析reslut,具体参考demo
    });
    await reverseGeoCodeSearch.reverseGeoCodeSearch(reverseGeoCodeSearchOption);
  }
  //根据输入的内容搜索相应的位置
  void searchLocation() async {
    viewModel.suggestList.clear();
    BMFSuggestionSearchOption suggestionSearchOption =
        BMFSuggestionSearchOption(
            keyword: viewModel.searchController.text, cityname: '北京市');
    BMFSuggestionSearch suggestionSearch = BMFSuggestionSearch();
    suggestionSearch.onGetSuggestSearchResult(callback:
        (BMFSuggestionSearchResult result, BMFSearchErrorCode errorCode) {
      if (result.suggestionList!.isNotEmpty) {
        //有搜索结果
        viewModel.suggestList.addAll(result.suggestionList!);
        for (int i = 0; i < result.suggestionList!.length; i++) {
          print("提醒位置:${result.suggestionList![i].key}");
        }
      } else {
        print("提醒位置:空");
      }
      notifyChanges();
    });
    await suggestionSearch.suggestionSearch(suggestionSearchOption);
  }
  void onSuggestChoose(int index) {
    //选中此项
    viewModel.suggestInfo = viewModel.suggestList[index];
    viewModel.poiChooseName = viewModel.suggestInfo.key!;
    //位置移动到此处
    viewModel.mapOptions = BMFMapOptions(
      center: BMFCoordinate(viewModel.suggestInfo.location!.latitude,
          viewModel.suggestInfo.location!.longitude),
      //zoomLevel: viewModel.mapLevel,
    );
    viewModel.mapController.updateMapOptions(viewModel.mapOptions);
    //关闭键盘
    SystemChannels.textInput.invokeMethod('TextInput.hide');
    //搜索周边poi
    getPoiByLocation(viewModel.suggestInfo.location!.latitude,
        viewModel.suggestInfo.location!.longitude);
    //搜索状态改为选择状态
    viewModel.isSearch = false;
    notifyChanges();
  }
}
状态管理自定义的变量
import 'package:flutter/cupertino.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:zuguantong/yuchuangbase/base/base_view_model.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
class MapChooseViewModel extends BaseViewModel {
  //搜索textfield的控制
  TextEditingController searchController = TextEditingController();
  //poi信息列表
  List<BMFPoiInfo> poiList = [];
  //搜索提示列表
  List<BMFSuggestionInfo> suggestList = [];
  //选中的搜索提示信息
  late BMFSuggestionInfo suggestInfo;
  //地图缩放等级
  int mapLevel = 12;
  //选中的位置信息
  late BMFPoiInfo poiInfo;
  //当前是否是搜索的状态
  bool isSearch = false;
  //临时选中的位置名称
  String poiChooseName = "";
  //地图默认的坐标(北京天安门)
  double lat = 39.917215;
  double lon = 116.380341;
  String x = "徐工225H摊铺机";
  //下面白色区域的高度
  double contentHieght = 350;
  //地图控制器
  late BMFMapController mapController;
  //地图初始化时候的Options
  late BMFMapOptions mapOptions = BMFMapOptions(
      center: BMFCoordinate(lat, lon),
      zoomLevel: 12,
      mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0));
}主要就是里面的几个核心的方法,我都抽离出来单独讲了,大家有问题可以留言交流,有错欢迎指正,感谢!!!


















