Google Maps Place Picker

一个Flutter插件,它通过 Open StreetHere MapsGoogle Maps 小部件提供“地点选择”。

非常感谢 Terry Kwon

该项目依赖以下包。

使用 Flutter 官方 google_maps_flutter 的地图;使用 Baseflow 的 geolocator 获取当前位置;使用 hadrienlejard 的 google_maps_webservice 进行地点和地理编码 API;使用 kevmoo 的 tuple 进行构建。

该包附带一个 Laravel (PHP 框架) 服务器,您可以在自己的服务器上托管。 服务器仓库链接在此…

预览

支持

如果该插件对您有用或节省了您的时间,请随时请我喝杯咖啡!☕️ 我获得的咖啡因越多,将来就能制作出更有用的项目。

Buy Me A Coffee

入门

  • https://cloud.google.com/maps-platform/ 获取 API 密钥。

  • 为每个平台启用 Google Map SDK。

    • 转到 Google Developers Console
    • 选择您要为其启用 Google Maps 的项目。
    • 选择导航菜单,然后选择“Google Maps”。
    • 在 Google Maps 菜单下选择“API”。
    • 要为 Android 启用 Google Maps,请在“其他 API”部分选择“Maps SDK for Android”,然后选择“启用”。
    • 要为 iOS 启用 Google Maps,请在“其他 API”部分选择“Maps SDK for iOS”,然后选择“启用”。
    • 确保您启用的 API 位于“已启用 API”部分。
  • 您还可以 在此处 找到开始使用 Google Maps Platform 的详细步骤。

Android

在应用程序清单 android/app/src/main/AndroidManifest.xml 中指定您的 API 密钥

<manifest ...
  <application ...
    <meta-data android:name="com.google.android.geo.API_KEY"
               android:value="YOUR KEY HERE"/>

注意: 从 3.0.0 版本开始,geolocator 插件已切换到 Android Support Libraries 的 AndroidX 版本。这意味着您需要确保您的 Android 项目也已升级以支持 AndroidX。详细说明可以在 此处 找到。

精简版是

  1. 将以下内容添加到您的“gradle.properties”文件中

android.useAndroidX=true
android.enableJetifier=true
  1. 确保将 `compileSdkVersion` 在您的 “android/app/build.gradle” 文件中设置为 28

android {
 compileSdkVersion 28

 ...
}
  1. 请确保将所有 android. 依赖项替换为其 AndroidX 对应项(完整列表可以在此处找到:https://developer.android.com.cn/jetpack/androidx/migrate)。

iOS

在应用程序代理 ios/Runner/AppDelegate.m 中指定您的 API 密钥

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#import "GoogleMaps/GoogleMaps.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GMSServices provideAPIKey:@"YOUR KEY HERE"];
  [GeneratedPluginRegistrant registerWithRegistry:self];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

或者在您的 Swift 代码中,在应用程序代理 ios/Runner/AppDelegate.swift 中指定您的 API 密钥

import UIKit
import Flutter
import GoogleMaps

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
  ) -> Bool {
    GMSServices.provideAPIKey("YOUR KEY HERE")
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

在 iOS 上,您需要将以下条目添加到 Info.plist 文件(位于 ios/Runner 下)才能访问设备的位置。

只需打开您的 Info.plist 文件并添加以下内容

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location when in the background.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to location when open and in the background.</string>

此外,您需要在 XCode 项目中添加 Background Modes 功能(项目 > ]] 签名与功能 ]] > “+ 功能”按钮),然后选择 Location Updates。g>此应用需要在打开和后台运行时访问位置。

通过向应用的 Info.plist 文件添加一个布尔属性(键为 io.flutter.embedded_views_preview,值为 YES)来选择加入嵌入式视图预览。

<key>io.flutter.embedded_views_preview</key>
<true/>

用法

基本用法

您可以通过 Navigator 推送到新页面来使用 PlacePicker,或者将其作为任何小部件的子项。当用户在地图上选择一个地点时,它将以 PickResult 类型返回结果到 'onPlacePicked'。或者,您可以使用 'selectedPlaceWidgetBuilder' 构建自己的方式并从中获取结果(请参阅下面的说明)。

Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => FlutterPlacePicker(
          apiKey: APIKeys.apiKey,   // Put YOUR OWN KEY here.
          onPlacePicked: (result) { 
            print(result.address); 
            Navigator.of(context).pop();
          },
          initialPosition: HomePage.kInitialPosition,
          useCurrentLocation: true,
        ),
      ),
    );

PickResult

参数 类型 描述
placeId 字符串 一个文本标识符,可唯一标识一个地点。要检索有关该地点的信息,请将此标识符作为 Places API 请求中的 placeId 字段。有关更多信息,请参阅 PlaceId
geometry 地理信息 包含结果的地理信息,通常包括地点的位置(geocode)和(可选)标识其大致覆盖范围的视口。
formattedAddress 字符串 一个包含此地点可读地址的字符串。此地址通常等同于“邮政地址”。
types List<String> 包含一个描述给定结果的功能类型数组。请参阅 支持的类型列表。XML 响应如果分配给结果的类型不止一种,则会包含多个元素。
addressComponents List<AddressComponent> 包含适用于此地址的各个组件的数组。

** 以下结果仅在进行自动补全搜索或通过拖动地图搜索时将 usePlaceDetailSearch 设置为 true 时获取。

PickResult (可选)

参数 类型 描述
adrAddress 字符串 该地点地址在 adr microformat 中的表示。
formattedPhoneNumber 字符串 包含该地点以其 本地格式 显示的电话号码
id 字符串 ? (Google 未记录 – 见下文更多信息)
reference 字符串 ? (Google 未记录 – 见下文更多信息)
icon 字符串 建议图标的 URL,可以在地图上指示此结果时显示给用户。
名称 字符串 返回结果的人性化名称
openingHours OpeningHoursDetail 营业时间信息
photos List<Photo> 照片对象数组,每个对象都包含一个图像引用
internationalPhoneNumber 字符串 该地点以国际格式显示的电话号码
priceLevel PriceLevel 该地点的价格级别,范围从 0 到 4。具体数值表示的金额因地区而异。
rating num 该地点的评分,从 1.0 到 5.0,基于汇总的用户评价。
scope 字符串
url 字符串 该地点官方 Google 页面的 URL。
vicinity 字符串 列出了该地点的一个简化地址,包括街道名称、门牌号和地区,但不包括省/州、邮政编码或国家。
utcOffset num 该地点当前时区相对于 UTC 的偏移量(以分钟为单位)
website 字符串 该地点的权威网站
reviews List<Review> 最多五个评价的 JSON 数组

有关结果的更多信息,请参阅 Google 文档。

PlacePicker

参数 类型 描述
apiKey 字符串 (必填) 您的 Google Maps API 密钥
onPlacePicked Callback(PickResult) 当用户选择地点并选择使用它时调用。如果您手动构建 ‘selectedPlaceWidgetBuilder’,此项将不会被调用,因为您将覆盖默认的“选择此处”按钮。
initialPosition LatLng (必填) 创建 Google Maps 时的初始中心位置。如果将 useCurrentLocation 设置为 true,它将尝试首先使用 GeoLocator 获取设备的当前位置。
useCurrentLocation 布尔值 是否使用设备当前位置作为初始中心位置。如果设置为 true 且用户允许收集其位置,则此项将代替 initial position。如果被拒绝,将使用 initialPosition。
desiredLocationAccuracy LocationAccuracy 获取当前位置的精度。默认为“高”。
hintText 字符串 搜索栏的提示文本
searchingText 字符串 搜索进行时出现的文本。默认为“正在搜索…”
proxyBaseUrl 字符串 用于 Google Maps 上的 API 调用。如果使用代理,可以设置 baseUrl。如果代理设置了 API 密钥,则不需要 apiKey。
httpClient Client 用于 Google Maps 上的 API 调用。用于需要身份验证或自定义配置的代理 URL。
autoCompleteDebounceInMilliseconds 整数 自动补全输入的防抖计时器。默认为 500
cameraMoveDebounceInMilliseconds 整数 通过拖动地图搜索地点的防抖计时器。默认为 750
intialMapType MapType Google Maps 的地图类型。默认为 normal。
enableMapTypeButton 布尔值 是否在地图上显示地图类型更改按钮
enableMyLocationButton 布尔值 是否在地图上显示我的位置按钮
usePinPointingSearch 布尔值 默认为 true。这将允许用户通过拖动地图来获取图钉指向的地点信息。
usePlaceDetailSearch 布尔值 默认为 false。将其设置为 true 将通过拖动地图搜索获取详细结果,但会向 Place Detail API 多发送一个请求。
onAutoCompleteFailed Callback(String) 自动补全搜索失败时调用
onGeocodingSearchFailed Callback(String) 通过拖动地图搜索地点失败时调用
onMapCreated MapCreatedCallback 创建时返回 Google Maps 控制器
selectedPlaceWidgetBuilder WidgetBuilder 在下面部分指定
pinBuilder WidgetBuilder 在下面部分指定
autocompleteOffset num 输入项中,服务用于匹配预测的最后一个字符的位置
autocompleteRadius num 返回地点结果的距离(以米为单位)
autocompleteLanguage 字符串 语言代码,指示结果应以何种语言返回(如果可能)。有关更多信息,请参阅 语言支持
autocompleteComponents List<Components> 您希望限制结果的地点分组。目前,您可以使用组件按最多 5 个国家/地区进行过滤。
autocompleteTypes List<String> 要返回的地点结果类型。请参阅 地点类型
strictbounds 布尔值 仅返回严格位于由位置和半径定义的区域内的地点。
region 字符串 region — 区域代码,指定为 ccTLD(国家代码顶级域名)两位字符值。大多数 ccTLD 代码与 ISO 3166-1 代码相同,但有一些例外。此参数只会影响搜索结果,而不会完全限制搜索结果。如果存在比指定区域更相关的结果,它们也可能被包含在内。当使用此参数时,结果中的 formatted_address 将省略指定区域的国家名称。
selectInitialPosition 布尔值 是否在初始地图加载时显示选定的地点。默认为 false。
resizeToAvoidBottomInset 布尔值 参考 Scaffold 的 resizeToAvoidBottomInset 属性。
initialSearchString 字符串 设置自动补全搜索的初始搜索字符串
searchForInitialValue 布尔值 启动时是否自动搜索初始值
forceAndroidLocationManager 布尔值 在 Android 设备上,您可以将其设置为 true,以强制 geolocator 插件使用 ‘LocationManager’ 来确定位置,而不是使用 ‘FusedLocationProviderClient’。在 iOS 上,此参数将被忽略。
myLocationButtonCooldown 整数 “myLocationButton”的冷却时间(以秒为单位)。默认为 10 秒。
forceSearchOnZoomChanged 布尔值 是否允许在缩放级别更改时进行地点搜索。默认为 false。
automaticallyImplyAppBarLeading 布尔值 默认情况下,顶部有一个返回按钮。将其设置为 false 将移除返回按钮。
autocompleteOnTrailingWhitespace 布尔值 是否允许自动补全在搜索末尾的空格上运行。默认为 false。参见问题 #54。

有关自动补全搜索的更多信息,请参阅 Google 文档。

自定义已选地点可视化

默认情况下,当用户通过自动补全搜索或拖动地图选择地点时,我们会在屏幕底部显示信息(FloatingCard)。

但是,如果您不喜欢此 UI/UX,只需使用“selectedPlaceWidgetBuilder”覆盖构建器即可。FlocatingCard 小部件可以重复使用,它会浮动在屏幕周围,或者您可以构建一个全新的小部件。它堆叠在地图上,因此您可能需要使用 Positioned 小部件。

请注意,使用此自定义将不会调用 [onPlacePicked] 回调,因为它将覆盖浮动卡上的默认“选择此处”按钮!

...
PlacePicker(apiKey: APIKeys.apiKey,
            ...
            selectedPlaceWidgetBuilder: (_, selectedPlace, state, isSearchBarFocused) {
              return isSearchBarFocused
                  ? Container()
                  // Use FloatingCard or just create your own Widget.
                  : FloatingCard(
                      bottomPosition: 0.0,    // MediaQuery.of(context) will cause rebuild. See MediaQuery document for the information.
                      leftPosition: 0.0,
                      rightPosition: 0.0,
                      width: 500,
                      borderRadius: BorderRadius.circular(12.0),
                      child: state == SearchingState.Searching ? 
                                      Center(child: CircularProgressIndicator()) : 
                                      RaisedButton(onPressed: () { print("do something with [selectedPlace] data"); },),
                   );
            },
            ...
          ),
...
参数 类型 描述
context BuildContext Flutter 的 build context 值
selectedPlace PickResult 用户选定地点的结果数据
state SearchingState 搜索操作的状态。(空闲、搜索中)
isSearchBarFocused 布尔值 搜索栏当前是否获得焦点,因此键盘已显示

自定义图钉

默认情况下,Pin 图标具有非常简单的动画效果,用于移动。但是,您也可以使用 ‘pinBuilder’ 创建自己的图钉 widget。

FlutterPlacePicker(apiKey: APIKeys.apiKey,
            ...
            pinBuilder: (context, state) {
                  if (state == PinState.Idle) {
                    return Icon(Icons.favorite_border);
                  } else {
                    return AnimatedIcon(.....);
                  }
                },
            ...                        
          ),
...
参数 类型 描述
context BuildContext Flutter 的 build context 值
state PinState 图钉的状态。(正在准备;地图加载时、空闲、拖动中)

更改默认 FloatingCard 的颜色

虽然您可以构建自己的预测列表项,但仍可以使用下面的 ThemeData 来更改默认列表项的样式。

// Light Theme
final ThemeData lightTheme = ThemeData.light().copyWith(
  // Background color of the FloatingCard
  cardColor: Colors.white,
  buttonTheme: ButtonThemeData(
    // Select here's button color
    buttonColor: Colors.black,
    textTheme: ButtonTextTheme.primary,
  ),
);

// Dark Theme
final ThemeData darkTheme = ThemeData.dark().copyWith(
  // Background color of the FloatingCard
  cardColor: Colors.grey,
  buttonTheme: ButtonThemeData(
    // Select here's button color
    buttonColor: Colors.yellow,
    textTheme: ButtonTextTheme.primary,
  ),
);

功能请求和问题

请在 问题跟踪器 中提交功能请求和错误。

Buy Me A Coffee

与我联系

Sam | Twitter Sam | LinkedIn Sam | Instagram

访问计数

Loading

GitHub

查看 Github