Flutter地点搜索与选择插件google_places_sdk的使用

Flutter 地点搜索与选择插件 google_places_sdk 的使用

google_places_sdk 是一个支持 Android 和 iOS 的 Flutter 插件,用于集成 Google Places SDK。本文将详细介绍如何使用该插件进行地点搜索和选择。

平台支持

Platform Version
Android SDK 16+
iOS iOS 11+
Web Not supported
Linux Not supported
MacOS Not supported
Windows Not supported

功能支持

Feature Android iOS
Soft Localization ✅ ❌
Device Localizations ✅ ✅
Place Autocomplete ✅ ✅
Place Details ✅ ✅
Current Place ❌ ❌
Place Photo ✅ ✅
Request Cancellation ✅ ❌

使用步骤

初始化插件

首先,需要在 pubspec.yaml 文件中添加 google_places 依赖,并初始化插件:

dependencies:
  google_places: ^latest_version

然后,在 Dart 代码中初始化插件:

await GooglePlaces().initialize(
  "Your maps key here",
  locale: const Locale('ar'),
);

更改语言环境

你可以更改语言环境(仅支持 Android):

await GooglePlaces().updateLocale(const Locale('en'));

获取自动补全结果

初始化插件后,可以通过查询获取自动补全搜索结果:

final List<AutocompletePrediction> results = await GooglePlaces().getAutoCompletePredictions('Great Pyramid of Giza');

参数说明:

  • query: 查询字符串(必需)
  • countryCodes: 国家代码列表(可选)
  • locationBias: 矩形边界(可选)
  • locationRestriction: 矩形限制(可选)
  • placeTypes: 地点类型列表(可选)
  • cancellationToken: 取消令牌(可选)

获取地点详情

通过传递地点 ID 获取地点详情:

final PlaceDetails placeDetails = await GooglePlaces.fetchPlaceDetails('Your Place Id');

参数说明:

  • placeId: 地点 ID(必需)
  • placeFields: 地点字段列表(可选)
  • cancellationToken: 取消令牌(可选)

获取地点照片

获取地点详情后,可以进一步获取地点的照片:

final Uint8List? photo = await GooglePlaces().fetchPlacePhoto(widget.metadata);

取消请求

插件支持取消对 Google API 的请求(仅支持 Android):

final token = CancellationToken();
final details = await GooglePlaces.fetchPlaceDetails('Your Place Id', cancellationToken: token);
await token.cancel();

注意:发出取消令牌并不能保证特定请求会被取消,即使没有返回响应,您仍可能被收费。

示例 Demo

以下是一个完整的示例应用,展示了如何使用 google_places_sdk 进行地点搜索和选择:

import 'dart:async';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:google_places_sdk/google_places_sdk.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  Future<void> initPlatformState() async {
    await GooglePlaces().initialize(
      const String.fromEnvironment("MAPS_KEY"),
      locale: const Locale('ar'),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Plugin example app')),
        body: const _HomePageContents(),
      ),
    );
  }
}

class _HomePageContents extends StatelessWidget {
  const _HomePageContents();

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
              onPressed: () => Navigator.push(context,
                  MaterialPageRoute(builder: (_) => const AutocompletePage())),
              child: const Text('Autocomplete'))
        ],
      ),
    );
  }
}

class AutocompletePage extends StatefulWidget {
  const AutocompletePage({super.key});

  @override
  State<AutocompletePage> createState() => _AutocompletePageState();
}

class _AutocompletePageState extends State<AutocompletePage> {
  final _predictions = <AutocompletePrediction>[];
  final _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Builder(
        builder: (context) {
          return Scaffold(
            appBar: AppBar(title: const Text('Plugin example app')),
            body: Center(
              child: ListView(
                children: [
                  Padding(
                    padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
                    child: TextField(
                      controller: _controller,
                      decoration: const InputDecoration(border: OutlineInputBorder()),
                      onSubmitted: (val) async {
                        GooglePlaces().getAutoCompletePredictions(val, countryCodes: ['SA']).then(
                          (value) => setState(() {
                            _predictions.clear();
                            _predictions.addAll(value);
                          }),
                        );
                      },
                    ),
                  ),
                  ElevatedButton(
                      onPressed: () async {
                        GooglePlaces().getAutoCompletePredictions(
                          _controller.text,
                          countryCodes: ['SA'],
                          placeTypes: [PlaceType.restaurant],
                        ).then((value) => setState(() {
                          _predictions.clear();
                          _predictions.addAll(value);
                        }));
                      },
                      child: const Text("Submit")),
                  ..._predictions.map(
                    (e) => AutoCompleteTile(prediction: e),
                  )
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

class AutoCompleteTile extends StatelessWidget {
  final AutocompletePrediction prediction;

  const AutoCompleteTile({super.key, required this.prediction});

  @override
  Widget build(BuildContext context) {
    return Table(
      children: [
        TableRow(children: [
          const Text('Place ID'),
          TextButton(
              onPressed: () {
                if (prediction.placeId == null) {
                  ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('Place Id not found')));
                }

                navigateToPage(context, placeId: prediction.placeId!);
              },
              child: Text(prediction.placeId ?? ''))
        ]),
        TableRow(children: [
          const Text('Full name'),
          Text(prediction.fullName ?? '')
        ]),
        TableRow(children: [
          const Text('Primary text'),
          Text(prediction.primaryText ?? '')
        ]),
        TableRow(children: [
          const Text('Secondary text'),
          Text(prediction.secondaryText ?? '')
        ]),
        TableRow(children: [const Text('PlaceTypes'), Container()]),
        ...prediction.placeTypes
            .map((t) => TableRow(children: [Container(), Text(t.value)]))
            .toList()
      ],
    );
  }

  void navigateToPage(BuildContext context, {required String placeId}) {
    Navigator.push(context,
        MaterialPageRoute(builder: (_) => PlaceDetailsPage(placeId: placeId)));
  }
}

class PlaceDetailsPage extends StatefulWidget {
  final String placeId;

  const PlaceDetailsPage({super.key, required this.placeId});

  @override
  State<StatefulWidget> createState() => _PlaceDetailsPageState();
}

class _PlaceDetailsPageState extends State<PlaceDetailsPage> {
  PlaceDetails? details;

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

  Future<void> initPlatformState() async {
    GooglePlaces()
        .fetchPlaceDetails(widget.placeId)
        .then((value) => setState(() => details = value));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(details?.name ?? '')),
      body: SingleChildScrollView(
        child: Table(
          children: [
            TableRow(children: [
              const Text('Place ID'),
              Text(details?.placeId ?? '')
            ]),
            TableRow(children: [const Text('Name'), Text(details?.name ?? '')]),
            TableRow(children: [
              const Text('Coords'),
              Text('(${details?.latLng?.lat}, ${details?.latLng?.lng})')
            ]),
            TableRow(children: [
              const Text('Address'),
              Text(details?.address ?? '')
            ]),
            TableRow(children: [
              const Text('Business Status'),
              Text(details?.businessStatus?.value ?? '')
            ]),
            TableRow(children: [
              const Text('Curbside Pickup'),
              Text(details?.curbsidePickup.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Delivery'),
              Text(details?.delivery.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Dine in'),
              Text(details?.dineIn.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Icon Background Color'),
              Text(details?.iconBackgroundColor.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Icon URL'),
              Text(details?.iconURL ?? '')
            ]),
            TableRow(children: [
              const Text('Opening Hours'),
              Text(details?.openingHours.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Phone Number'),
              Text(details?.phoneNumber ?? '')
            ]),
            TableRow(children: [const Text('Photos Metadata'), Container()]),
            ...details?.photoMetadatas
                    ?.map((m) => TableRow(children: [
                          Container(),
                          PhotoMetadataTile(metadata: m)
                        ]))
                    .toList() ??
                [],
            TableRow(children: [
              const Text('Plus Code'),
              Text(details?.plusCode.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Price Level'),
              Text(details?.priceLevel?.id.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Rating'),
              Text(details?.rating.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Place Types'),
              Text(details?.placeTypes.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('User Rating Total'),
              Text(details?.userRatingTotal.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('UTC Offset Minutes'),
              Text(details?.utcOffsetMinutes.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Viewport'),
              Text(details?.viewport.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Website URI'),
              Text(details?.websiteURI?.toString() ?? '')
            ]),
            TableRow(children: [
              const Text('Is Open'),
              Text(details?.isOpen.toString() ?? '')
            ]),
          ],
        ),
      ),
    );
  }
}

class PhotoMetadataTile extends StatelessWidget {
  final PhotoMetadata metadata;

  const PhotoMetadataTile({super.key, required this.metadata});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('$metadata'),
        TextButton(
          onPressed: () => Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (_) => PlacePhotoPage(metadata: metadata))),
          child: const Text('Show photo'),
        )
      ],
    );
  }
}

class PlacePhotoPage extends StatefulWidget {
  final PhotoMetadata metadata;

  const PlacePhotoPage({super.key, required this.metadata});

  @override
  State<StatefulWidget> createState() => _PlacePhotoPageState();
}

class _PlacePhotoPageState extends State<PlacePhotoPage> {
  Uint8List? photo;

  @override
  void initState() {
    super.initState();
    GooglePlaces()
        .fetchPlacePhoto(widget.metadata)
        .then((value) => setState(() => photo = value));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Photo')),
      body: Center(
        child: photo == null ? const Text('No image yet') : Image.memory(photo!),
      ),
    );
  }
}

这个示例应用展示了如何使用 google_places_sdk 插件进行地点搜索、获取地点详情以及显示地点照片。希望对你有所帮助!


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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用google_places_sdk插件来实现地点搜索与选择的代码示例。这个插件允许你集成Google Places API,从而提供地点搜索和自动完成功能。

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

dependencies:
  flutter:
    sdk: flutter
  google_places_sdk: ^x.y.z  # 请替换为最新版本号

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

接下来,你需要配置Google Places API。确保你已经在Google Cloud Platform上启用了Places API,并创建了API密钥。然后,在你的android/app/src/main/AndroidManifest.xml文件中添加以下权限和API密钥配置:

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

    <!-- 添加必要的权限 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        ... >
        
        <!-- 添加API密钥的meta-data -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR_API_KEY_HERE" />

        ...
    </application>
</manifest>

确保将YOUR_API_KEY_HERE替换为你的实际API密钥。

现在,你可以在你的Flutter代码中实现地点搜索和选择功能。以下是一个简单的示例:

import 'package:flutter/material.dart';
import 'package:google_places_sdk/google_places_sdk.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: PlacePickerScreen(),
    );
  }
}

class PlacePickerScreen extends StatefulWidget {
  @override
  _PlacePickerScreenState createState() => _PlacePickerScreenState();
}

class _PlacePickerScreenState extends State<PlacePickerScreen> {
  late PlacesClient _placesClient;
  late AutocompleteSessionToken _sessionToken;

  @override
  void initState() {
    super.initState();
    _placesClient = PlacesClient.create();
    _sessionToken = AutocompleteSessionToken.current();
  }

  void _onPlaceSelected(Place place) {
    // 处理选中的地点
    print('Selected place: ${place.name}, ${place.placeId}');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('地点搜索与选择'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            try {
              Prediction? prediction = await _placesClient.autocompleteQuery(
                AutocompleteRequest(
                  sessionToken: _sessionToken,
                  input: '餐厅',
                  type: PlaceType.restaurant,
                ),
              );

              if (prediction != null) {
                // 获取地点的详细信息
                Place? place = await _placesClient.getPlaceDetails(
                  PlaceDetailsRequest(placeId: prediction.placeId),
                );

                if (place != null) {
                  _onPlaceSelected(place);
                }
              }
            } catch (e) {
              print('Error: $e');
            }
          },
          child: Text('搜索餐厅'),
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个按钮,用于触发地点搜索。当用户点击按钮时,应用将使用google_places_sdk插件来搜索与“餐厅”相关的地点,并在控制台中打印选中地点的名称和ID。

请注意,这个示例仅展示了基本的搜索功能。在实际应用中,你可能需要处理更多的细节,例如用户权限请求、错误处理、UI设计等。此外,请确保你的API密钥受到适当的保护,避免在客户端代码中硬编码API密钥,最好使用更安全的方法来管理API密钥。

回到顶部