Flutter地点选择插件google_maps_places_picker_refractored的使用

Flutter地点选择插件google_maps_places_picker_refractored的使用

Google Maps Places Picker Refractored

这是一个对fysoul17/google_maps_place_picker插件的分叉版本,增加了自定义样式和功能。所有功劳归于原始开发者。

该插件通过Flutter的google_maps_flutter小部件提供了一个“地点选择”功能。

项目依赖以下包:

  • 使用Flutter官方的google_maps_flutter绘制地图。
  • 使用Baseflow的geolocator获取当前位置。
  • 使用hadrienlejard的google_maps_webservice调用Place和Geocoding API。
  • 使用kevmoo的tuple构建器。

预览


支持

如果本插件对你有用或节省了你的时间,请考虑买杯咖啡支持我!
更多咖啡意味着未来能制作更有用的项目。

Buy Me A Coffee


入门指南

获取API密钥

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

启用Google Maps SDK

  1. 前往Google开发者控制台
  2. 选择你要启用Google Maps的项目。
  3. 在导航菜单中选择“Google Maps”。
  4. 点击“APIs”,然后在“附加APIs”部分启用以下选项:
    • 对于Android,启用“Maps SDK for Android”。
    • 对于iOS,启用“Maps SDK for iOS”。
  5. 确保启用的API出现在“已启用APIs”部分。

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插件切换到了AndroidX版本的支持库。这意味着你需要确保你的Android项目也升级到支持AndroidX。详细说明可以参考这里

以下是快速解决步骤:

  1. gradle.properties文件中添加以下内容:
    android.useAndroidX=true
    android.enableJetifier=true
    
  2. android/app/build.gradle文件中的compileSdkVersion设置为28:
    android {
      compileSdkVersion 28
    
      ...
    }
    
  3. 替换所有android.依赖项为对应的AndroidX版本(完整列表可参考这里)。

iOS配置

在应用委托文件ios/Runner/AppDelegate.m中添加API密钥:

#import "AppDelegate.h"
#import "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代码中,添加API密钥到ios/Runner/AppDelegate.swift

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文件中添加以下键值对:

<key>NSLocationWhenInUseUsageDescription</key>
<string>此应用需要在打开时访问您的位置。</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>此应用需要在后台访问您的位置。</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>此应用需要在打开和后台时访问您的位置。</string>

此外,你需要在XCode项目中启用Background Modes能力,并选择Location Updates

为了嵌入视图预览,向应用的Info.plist文件中添加一个布尔属性,键为io.flutter.embedded_views_preview,值为YES

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

使用方法

基本用法

你可以通过Navigator.push跳转到新页面,也可以将PlacePicker作为任何小部件的子组件。

当用户在地图上选择一个地点时,它会通过onPlacePicked回调返回PickResult类型的结果。你也可以通过selectedPlaceWidgetBuilder构建自己的方式来获取结果。

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => PlacePicker(
      apiKey: APIKeys.apiKey,   // 替换为你的API密钥
      onPlacePicked: (result) {
        print(result.address); // 打印选中的地址
        Navigator.of(context).pop();
      },
      initialPosition: HomePage.kInitialPosition, // 初始地图中心位置
      useCurrentLocation: true, // 是否使用设备当前位置
    ),
  ),
);

PickResult 类型

PickResult包含以下字段:

参数 类型 描述
placeId String 地点的唯一标识符,用于获取地点详情。
geometry Geometry 包含地点的几何信息,如位置和视口。
formattedAddress String 地点的人类可读地址。
types List<String> 地点类型的数组。
addressComponents List<AddressComponent> 地址的组成部分数组。

可选字段(仅在启用自动补全搜索或usePlaceDetailSearchtrue时返回):

参数 类型 描述
adrAddress String 地点的adr微格式表示。
formattedPhoneNumber String 地点的本地格式电话号码。
id String 地点的唯一ID(未在Google文档中记录)。
reference String 地点的引用(未在Google文档中记录)。
icon String 地点图标URL。
name String 地点的名称。
openingHours OpeningHoursDetail 地点的营业时间信息。
photos List<Photo> 地点的照片数组。
internationalPhoneNumber String 地点的国际格式电话号码。
priceLevel PriceLevel 地点的价格等级(0-4)。
rating num 地点的评分(1.0-5.0)。
url String 地点的官方Google页面URL。
vicinity String 简化版地址,包括街道名、编号和城市,但不包括省份、邮政编码或国家。
utcOffset num 地点当前时区与UTC的偏移量(分钟)。
website String 地点的权威网站。
reviews List<Review> 地点的最多五条评论。

更多关于PickResult的信息可以参考Google文档

PlacePicker 参数

PlacePicker支持以下参数:

参数 类型 描述
apiKey String 必填,Google Maps API密钥。
onPlacePicked Callback(PickResult) 当用户选择地点并确认时触发。
initialPosition LatLng 必填,初始地图中心位置。如果useCurrentLocationtrue,则尝试获取设备当前位置。
useCurrentLocation bool 是否使用设备当前位置作为初始中心位置。
desiredLocationAccuracy LocationAccuracy 获取当前位置的精度,默认为高精度。
hintText String 搜索栏提示文本。
searchingText String 搜索进行时显示的文本,默认为“Searching…”。
proxyBaseUrl String 用于Google Maps API调用的代理基础URL。
httpClient Client 用于Google Maps API调用的HTTP客户端。
autoCompleteDebounceInMilliseconds int 自动补全输入的去抖动时间,默认为500毫秒。
cameraMoveDebounceInMilliseconds int 地图拖动时搜索的去抖动时间,默认为750毫秒。
intialMapType MapType 地图类型,默认为普通地图。
enableMapTypeButton bool 是否显示地图类型切换按钮。
enableMyLocationButton bool 是否显示我的位置按钮。
usePinPointingSearch bool 默认为true,允许用户拖动地图以获取指针指向位置的信息。
usePlaceDetailSearch bool 默认为false,设置为true时,拖动地图搜索会获取更详细的地点信息,但会额外消耗一个Place Details API请求。
onAutoCompleteFailed Callback(String) 自动补全搜索失败时触发。
onGeocodingSearchFailed Callback(String) 拖动地图搜索失败时触发。
onMapCreated MapCreatedCallback 地图创建完成后返回控制器。
selectedPlaceWidgetBuilder WidgetBuilder 自定义选中地点的UI。
pinBuilder WidgetBuilder 自定义地图上的指针。
autocompleteOffset num 自动补全搜索的偏移量。
autocompleteRadius num 自动补全搜索的半径范围。
autocompleteLanguage String 自动补全搜索的语言代码。
autocompleteComponents List<Components> 限制搜索结果的区域。
autocompleteTypes List<String> 限制搜索结果的地点类型。
strictbounds bool 仅返回严格位于指定区域内的地点。
region String 限制搜索结果的国家代码。
selectInitialPosition bool 是否在初始加载时显示选中的地点。
resizeToAvoidBottomInset bool 参考Scaffold的resizeToAvoidBottomInset属性。
initialSearchString String 设置初始搜索字符串。
searchForInitialValue bool 是否在启动时自动搜索初始值。
forceAndroidLocationManager bool 强制Android设备使用LocationManager获取位置。
myLocationButtonCooldown int “我的位置”按钮的冷却时间,默认为10秒。
forceSearchOnZoomChanged bool 是否允许在缩放变化时进行搜索,默认为false
automaticallyImplyAppBarLeading bool 是否默认显示返回按钮,默认为true
autocompleteOnTrailingWhitespace bool 是否允许在搜索末尾空格时进行自动补全,默认为false
All Of the input decoration props InputDecoration 样式搜索文本框。
borderRadius BorderRadiusGeometry? 输入框容器的圆角。
isInScaffoldBodyAndHasAppBar bool (default=true) 是否将地点选择器包裹在带有AppBar的Scaffold中。
height double? 输入框容器的高度。

更多关于自动补全搜索的信息可以参考Google文档


自定义选中地点的可视化

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

如果你不喜欢这种UI/UX,可以通过selectedPlaceWidgetBuilder覆盖默认的构建器。可以重用FloatingCard或完全根据需求构建新的小部件。它会叠加在地图上,因此建议使用Positioned小部件。

注意:使用此自定义方法不会触发onPlacePicked回调,因为它会覆盖默认的“选择这里”按钮。

PlacePicker(
  apiKey: APIKeys.apiKey,
  ...
  selectedPlaceWidgetBuilder: (_, selectedPlace, state, isSearchBarFocused) {
    return isSearchBarFocused
        ? Container()
        : FloatingCard(
            bottomPosition: 0.0,
            leftPosition: 0.0,
            rightPosition: 0.0,
            width: 500,
            borderRadius: BorderRadius.circular(12.0),
            child: state == SearchingState.Searching
                ? Center(child: CircularProgressIndicator())
                : ElevatedButton(
                    onPressed: () {
                      print("do something with [selectedPlace] data");
                    },
                    child: Text("Pick Here"),
                  ),
          );
  },
  ...
),

参数说明:

参数 类型 描述
context BuildContext Flutter的构建上下文值。
selectedPlace PickResult 用户选择地点的结果数据。
state SearchingState 搜索操作的状态(Idle, Searching)。
isSearchBarFocused bool 是否搜索栏处于聚焦状态,键盘是否显示。

自定义地图指针

默认情况下,地图指针具有简单的移动动画。你可以通过pinBuilder创建自定义指针。

PlacePicker(
  apiKey: APIKeys.apiKey,
  ...
  pinBuilder: (context, state) {
    if (state == PinState.Idle) {
      return Icon(Icons.favorite_border);
    } else {
      return AnimatedIcon(...);
    }
  },
  ...
),

参数说明:

参数 类型 描述
context BuildContext Flutter的构建上下文值。
state PinState 指针的状态(Preparing, Idle, Dragging)。

更改默认FloatingCard的颜色

即使你构建了自己的预测小部件,仍然可以通过themeData更改默认小部件的样式。

// 浅色主题
final ThemeData lightTheme = ThemeData.light().copyWith(
  cardColor: Colors.white, // FloatingCard背景颜色
  buttonTheme: ButtonThemeData(
    buttonColor: Colors.black, // 选择按钮颜色
    textTheme: ButtonTextTheme.primary,
  ),
);

// 深色主题
final ThemeData darkTheme = ThemeData.dark().copyWith(
  cardColor: Colors.grey, // FloatingCard背景颜色
  buttonTheme: ButtonThemeData(
    buttonColor: Colors.yellow, // 选择按钮颜色
    textTheme: ButtonTextTheme.primary,
  ),
);

更多关于Flutter地点选择插件google_maps_places_picker_refractored的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地点选择插件google_maps_places_picker_refractored的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


google_maps_places_picker_refractored 是一个用于在 Flutter 应用中选择地点的插件,它基于 Google Maps Places API。该插件允许用户通过地图选择地点,并返回地点的详细信息。

安装插件

首先,你需要在 pubspec.yaml 文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  google_maps_places_picker_refractored: ^1.0.0

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

获取 Google Maps API 密钥

在使用该插件之前,你需要获取一个 Google Maps API 密钥。你可以在 Google Cloud Console 中创建一个项目并启用 Google Maps JavaScript API 和 Places API。

配置 Android 和 iOS

Android:

android/app/src/main/AndroidManifest.xml 文件中添加以下代码:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <application
        android:name=".MyApplication"
        android:label="MyApp"
        android:icon="@mipmap/ic_launcher">

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR_GOOGLE_MAPS_API_KEY"/>

    </application>
</manifest>

iOS:

ios/Runner/AppDelegate.swift 文件中添加以下代码:

import UIKit
import Flutter
import GoogleMaps

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

使用插件

在你的 Flutter 应用中,你可以使用 GoogleMapsPlacesPicker 来打开地点选择器,并获取用户选择的地点信息。

import 'package:flutter/material.dart';
import 'package:google_maps_places_picker_refractored/google_maps_places_picker.dart';
import 'package:google_maps_places_picker_refractored/providers/place_provider.dart';
import 'package:google_maps_places_picker_refractored/models/pick_result.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Google Maps Place Picker',
      home: PlacePickerExample(),
    );
  }
}

class PlacePickerExample extends StatefulWidget {
  [@override](/user/override)
  _PlacePickerExampleState createState() => _PlacePickerExampleState();
}

class _PlacePickerExampleState extends State<PlacePickerExample> {
  PickResult? selectedPlace;

  void _openPlacePicker() async {
    PickResult? result = await Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => PlacePicker(
          apiKey: "YOUR_GOOGLE_MAPS_API_KEY",
          onPlacePicked: (PickResult result) {
            Navigator.of(context).pop();
            setState(() {
              selectedPlace = result;
            });
          },
          initialPosition: LatLng(37.4219999, -122.0862462), // 初始位置
          useCurrentLocation: true, // 是否使用当前定位
        ),
      ),
    );

    if (result != null) {
      setState(() {
        selectedPlace = result;
      });
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Google Maps Place Picker'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            if (selectedPlace != null)
              Text('Selected Place: ${selectedPlace!.formattedAddress}'),
            ElevatedButton(
              onPressed: _openPlacePicker,
              child: Text('Pick a Place'),
            ),
          ],
        ),
      ),
    );
  }
}
回到顶部