Flutter谷歌地点搜索插件google_places_flutter_api的使用

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

Flutter谷歌地点搜索插件google_places_flutter_api的使用

简介

google_places_flutter_api 是一个用于Flutter应用的Google Places自动补全插件。它可以帮助用户在应用中快速查找和选择地点。需要注意的是,使用Google Places API需要在Google Cloud平台上启用计费功能。

安装

在项目的 pubspec.yaml 文件的 dependencies 部分添加以下内容:

dependencies:
  google_places_flutter_api: <latest_version>

请确保将 <latest_version> 替换为最新的版本号。

使用方法

以下是一个完整的示例代码,展示了如何使用 google_places_flutter_api 插件来实现地点搜索功能。

import 'dart:async';
import 'dart:math';

import 'package:google_api_headers/google_api_headers.dart';
import 'package:flutter/material.dart';
import 'package:google_places_flutter_api/google_places_flutter_api.dart';
import 'package:flutter_google_maps_webservices/places.dart';

const kGoogleApiKey = "API_KEY"; // 替换为你的Google API密钥

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

final customTheme = ThemeData(
  brightness: Brightness.dark,
  inputDecorationTheme: const InputDecorationTheme(
    border: OutlineInputBorder(
      borderRadius: BorderRadius.all(Radius.circular(4.00)),
    ),
    contentPadding: EdgeInsets.symmetric(
      vertical: 12.50,
      horizontal: 10.00,
    ),
  ),
);

class RoutesWidget extends StatelessWidget {
  const RoutesWidget({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) => MaterialApp(
        title: "My App",
        darkTheme: customTheme,
        themeMode: ThemeMode.dark,
        routes: {
          "/": (_) => const MyApp(),
          "/search": (_) => CustomSearchScaffold(),
        },
      );
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

final homeScaffoldKey = GlobalKey<ScaffoldState>();
final searchScaffoldKey = GlobalKey<ScaffoldState>();

class _MyAppState extends State<MyApp> {
  Mode? _mode = Mode.overlay;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      key: homeScaffoldKey,
      appBar: AppBar(
        title: const Text("My App"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _buildDropdownMenu(),
            ElevatedButton(
              onPressed: _handlePressButton,
              child: const Text("Search places"),
            ),
            ElevatedButton(
              child: const Text("Custom"),
              onPressed: () {
                Navigator.of(context).pushNamed("/search");
              },
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildDropdownMenu() => DropdownButton(
        value: _mode,
        items: const <DropdownMenuItem<Mode>>[
          DropdownMenuItem<Mode>(
            child: Text("Overlay"),
            value: Mode.overlay,
          ),
          DropdownMenuItem<Mode>(
            child: Text("Fullscreen"),
            value: Mode.fullscreen,
          ),
        ],
        onChanged: (dynamic m) {
          setState(() {
            _mode = m;
          });
        },
      );

  void onError(PlacesAutocompleteResponse response) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(response.errorMessage!)),
    );
  }

  Future<void> _handlePressButton() async {
    // 显示自动补全输入框并获取用户选择的预测结果
    Prediction? p = await PlacesAutocomplete.show(
      context: context,
      apiKey: kGoogleApiKey,
      onError: onError,
      mode: _mode!,
      language: "fr", // 设置语言为法语
      decoration: InputDecoration(
        hintText: 'Search',
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(20),
          borderSide: const BorderSide(
            color: Colors.white,
          ),
        ),
      ),
      components: [Component(Component.country, "fr")], // 限制搜索范围为法国
    );

    if (p != null) {
      displayPrediction(p, context);
    }
  }
}

Future<void> displayPrediction(Prediction? p, BuildContext context) async {
  if (p != null) {
    // 获取地点详情(纬度/经度)
    GoogleMapsPlaces _places = GoogleMapsPlaces(
      apiKey: kGoogleApiKey,
      apiHeaders: await const GoogleApiHeaders().getHeaders(),
    );
    PlacesDetailsResponse detail =
        await _places.getDetailsByPlaceId(p.placeId!);
    final lat = detail.result.geometry!.location.lat;
    final lng = detail.result.geometry!.location.lng;

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text("${p.description} - $lat/$lng")),
    );
  }
}

// 自定义搜索页面,继承自 PlacesAutocompleteWidget
class CustomSearchScaffold extends PlacesAutocompleteWidget {
  CustomSearchScaffold({Key? key})
      : super(
          key: key,
          apiKey: kGoogleApiKey,
          sessionToken: Uuid().generateV4(),
          language: "en", // 设置语言为英语
          components: [Component(Component.country, "uk")], // 限制搜索范围为英国
        );

  [@override](/user/override)
  _CustomSearchScaffoldState createState() => _CustomSearchScaffoldState();
}

class _CustomSearchScaffoldState extends PlacesAutocompleteState {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final appBar = AppBar(title: const AppBarPlacesAutoCompleteTextField());
    final body = PlacesAutocompleteResult(
      onTap: (p) {
        displayPrediction(p, context);
      },
      logo: const Row(
        children: [FlutterLogo()],
        mainAxisAlignment: MainAxisAlignment.center,
      ),
    );
    return Scaffold(key: searchScaffoldKey, appBar: appBar, body: body);
  }

  [@override](/user/override)
  void onResponseError(PlacesAutocompleteResponse response) {
    super.onResponseError(response);
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(response.errorMessage!)),
    );
  }

  [@override](/user/override)
  void onResponse(PlacesAutocompleteResponse? response) {
    super.onResponse(response);
    if (response != null && response.predictions.isNotEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text("Got answer")),
      );
    }
  }
}

class Uuid {
  final Random _random = Random();

  String generateV4() {
    // 生成UUID v4格式的字符串
    final int special = 8 + _random.nextInt(4);

    return '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-'
        '${_bitsDigits(16, 4)}-'
        '4${_bitsDigits(12, 3)}-'
        '${_printDigits(special, 1)}${_bitsDigits(12, 3)}-'
        '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}';
  }

  String _bitsDigits(int bitCount, int digitCount) =>
      _printDigits(_generateBits(bitCount), digitCount);

  int _generateBits(int bitCount) => _random.nextInt(1 << bitCount);

  String _printDigits(int value, int count) =>
      value.toRadixString(16).padLeft(count, '0');
}

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

1 回复

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


当然,以下是一个关于如何使用 google_places_flutter_api 插件在 Flutter 中进行谷歌地点搜索的示例代码。这个插件允许你使用 Google Places API 来搜索地点、获取地点详细信息等。

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

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

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

接下来,你需要在 Android 和 iOS 项目中配置 Google Places API 的密钥。

Android 配置

  1. android/app/src/main/AndroidManifest.xml 中添加以下权限:
<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" />
  1. android/app/build.gradle 中添加 Google Places 服务的依赖:
dependencies {
    implementation 'com.google.android.libraries.places:places:2.4.0'  // 请根据需要替换为最新版本
}
  1. 在 Firebase 项目中启用 Places API 并获取 API 密钥,然后在 android/app/src/main/res/values/strings.xml 中添加你的 API 密钥(注意:出于安全考虑,最好不要在代码中硬编码 API 密钥,而是使用环境变量或 Firebase 配置):
<resources>
    <string name="google_api_key">YOUR_API_KEY_HERE</string>
</resources>

iOS 配置

  1. 在你的 iOS 项目中,打开 Info.plist 并添加以下权限:
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要访问您的位置以搜索地点</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>需要始终访问您的位置以搜索地点</string>
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
  1. 在 Firebase 项目中启用 Places API 并获取 API 密钥,然后在你的 iOS 项目中的某个地方(如 AppDelegate.swiftInfo.plist 的自定义配置中)存储你的 API 密钥。

Flutter 代码示例

以下是一个简单的 Flutter 示例,展示如何使用 google_places_flutter_api 进行地点搜索:

import 'package:flutter/material.dart';
import 'package:google_places_flutter_api/models/predictions.dart';
import 'package:google_places_flutter_api/models/place_details.dart';
import 'package:google_places_flutter_api/google_places_flutter_api.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Google Places Flutter API Example'),
        ),
        body: PlaceSearchScreen(),
      ),
    );
  }
}

class PlaceSearchScreen extends StatefulWidget {
  @override
  _PlaceSearchScreenState createState() => _PlaceSearchScreenState();
}

class _PlaceSearchScreenState extends State<PlaceSearchScreen> {
  final _placesController = TextEditingController();
  List<Prediction> _predictions = [];
  PlaceDetails? _placeDetails;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          TextField(
            controller: _placesController,
            decoration: InputDecoration(
              prefixIcon: Icon(Icons.search),
              labelText: 'Search for a place',
              suffixIcon: IconButton(
                icon: Icon(Icons.search),
                onPressed: _searchPlaces,
              ),
            ),
          ),
          SizedBox(height: 16),
          Expanded(
            child: _predictions.isEmpty
                ? Center(child: Text('No predictions found'))
                : ListView.builder(
                    itemCount: _predictions.length,
                    itemBuilder: (context, index) {
                      Prediction prediction = _predictions[index];
                      return ListTile(
                        title: Text(prediction.description ?? ''),
                        subtitle: Text(prediction.placeId ?? ''),
                        onTap: () => _getPlaceDetails(prediction.placeId ?? ''),
                      );
                    }),
          ),
          if (_placeDetails != null)
            Padding(
              padding: const EdgeInsets.symmetric(vertical: 16.0),
              child: Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Place Name: ${_placeDetails!.name}'),
                      Text('Place Address: ${_placeDetails!.formattedAddress}'),
                      Text('Place Rating: ${_placeDetails!.rating?.toString()}'),
                    ],
                  ),
                ),
              ),
            ),
        ],
      ),
    );
  }

  Future<void> _searchPlaces() async {
    if (_placesController.text.isEmpty) {
      setState(() {
        _predictions = [];
      });
      return;
    }

    try {
      final predictionsResult = await GooglePlacesApiClient.placesAutocomplete(
        input: _placesController.text,
        apiKey: 'YOUR_API_KEY_HERE',  // 替换为你的API密钥
        sessionToken: 'SESSION_TOKEN',  // 可选,用于跟踪会话
        components: 'country:us',  // 可选,用于限制搜索范围
        type: 'geocode',  // 可选,用于指定搜索类型
      );

      setState(() {
        _predictions = predictionsResult.predictions ?? [];
      });
    } catch (e) {
      print('Error searching places: $e');
    }
  }

  Future<void> _getPlaceDetails(String placeId) async {
    try {
      final placeDetailsResult = await GooglePlacesApiClient.getPlaceDetails(
        placeId: placeId,
        apiKey: 'YOUR_API_KEY_HERE',  // 替换为你的API密钥
        fields: 'name,rating,formatted_address',  // 可选,指定要返回的字段
      );

      setState(() {
        _placeDetails = placeDetailsResult.result;
      });
    } catch (e) {
      print('Error getting place details: $e');
    }
  }
}

请注意,在上面的代码中,你需要将 'YOUR_API_KEY_HERE' 替换为你的实际 Google Places API 密钥。同时,出于安全考虑,不建议在客户端代码中硬编码 API 密钥。你可以考虑使用环境变量或 Firebase 配置来管理 API 密钥。

此外,这个示例代码使用了 GooglePlacesApiClient 的静态方法来进行地点搜索和获取地点详细信息。确保你按照插件的文档正确初始化和配置客户端。

希望这个示例能帮到你!如果有任何问题,请随时提问。

回到顶部