Google Maps Place Picker
一个Flutter插件,它通过 Open Street、Here Maps 和 Google Maps 小部件提供“地点选择”。
非常感谢 Terry Kwon
该项目依赖以下包。
使用 Flutter 官方 google_maps_flutter 的地图;使用 Baseflow 的 geolocator 获取当前位置;使用 hadrienlejard 的 google_maps_webservice 进行地点和地理编码 API;使用 kevmoo 的 tuple 进行构建。
该包附带一个 Laravel (PHP 框架) 服务器,您可以在自己的服务器上托管。 服务器仓库链接在此…
预览
支持
如果该插件对您有用或节省了您的时间,请随时请我喝杯咖啡!☕️ 我获得的咖啡因越多,将来就能制作出更有用的项目。
入门
-
在 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。详细说明可以在 此处 找到。
精简版是
- 将以下内容添加到您的“gradle.properties”文件中
android.useAndroidX=true android.enableJetifier=true
- 确保将 `compileSdkVersion` 在您的 “android/app/build.gradle” 文件中设置为 28
android { compileSdkVersion 28 ... }
- 请确保将所有
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,
),
);
功能请求和问题
请在 问题跟踪器 中提交功能请求和错误。


