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

发布于 1周前 作者 gougou168 来自 Flutter

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密钥。

回到顶部