Flutter谷歌地点服务插件google_place_plus的使用

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

Flutter谷歌地点服务插件google_place_plus的使用

简介

google_place_plus 是一个支持iOS、Android和Web平台的Flutter插件,提供了Google Places API的功能。该插件是从 bazrafkan/google_place 分支出来的,并对其进行了改进。

功能预览

通过HTTP请求,Google Places API可以返回关于地点的信息。这些地点包括商家、地理位置或著名的兴趣点。以下是一些可用的API请求:

  1. Place Search:根据用户的位置或搜索字符串返回地点列表。
  2. Place Details:返回特定地点的详细信息,包括用户评论。
  3. Place Photos:提供访问存储在Google Place数据库中的数百万张与地点相关的照片。
  4. Place Autocomplete:当用户输入时自动填写地点名称和/或地址。
  5. Query Autocomplete:为基于文本的地理搜索提供查询预测服务,在用户输入时返回建议查询。

使用方法

要在项目中使用此插件,请将 google_place_plus 添加为依赖项到您的 pubspec.yaml 文件中。

获取API密钥

首先,您需要从 Google Cloud Console 获取API密钥:

  • 登录Google开发者控制台。
  • 选择您要启用Google Place的项目。

示例代码

下面是一个完整的示例demo,展示了如何在Flutter应用中使用 google_place_plus 插件进行地点自动完成搜索并显示地点详情。

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:google_place_plus/google_place_plus.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await dotenv.load(fileName: ".env");
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

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

class _HomePageState extends State<HomePage> {
  GooglePlace? googlePlace;
  List<AutocompletePrediction> predictions = [];

  [@override](/user/override)
  void initState() {
    String? apiKey = dotenv.env['API_KEY'];
    if (apiKey != null) googlePlace = GooglePlace(apiKey);
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Container(
          margin: EdgeInsets.only(right: 20, left: 20, top: 20),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              TextField(
                decoration: InputDecoration(
                  labelText: "Search",
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.blue, width: 2.0),
                  ),
                  enabledBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black54, width: 2.0),
                  ),
                ),
                onChanged: (value) {
                  if (value.isNotEmpty) {
                    autoCompleteSearch(value);
                  } else {
                    if (predictions.length > 0 && mounted) {
                      setState(() {
                        predictions = [];
                      });
                    }
                  }
                },
              ),
              SizedBox(height: 10),
              Expanded(
                child: ListView.builder(
                  itemCount: predictions.length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      leading: CircleAvatar(
                        child: Icon(Icons.pin_drop, color: Colors.white),
                      ),
                      title: Text(predictions[index].description ?? ''),
                      onTap: () {
                        debugPrint(predictions[index].placeId);
                        if (googlePlace == null) return;
                        if (predictions[index].placeId == null) return;
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => DetailsPage(
                              placeId: predictions[index].placeId!,
                              googlePlace: googlePlace!,
                            ),
                          ),
                        );
                      },
                    );
                  },
                ),
              ),
              Container(
                margin: EdgeInsets.only(top: 10, bottom: 10),
                child: Image.asset("assets/powered_by_google.png"),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void autoCompleteSearch(String value) async {
    var result = await googlePlace?.autocomplete.get(value);
    if (result != null && result.predictions != null && mounted) {
      setState(() {
        predictions = result.predictions!;
      });
    }
  }
}

class DetailsPage extends StatefulWidget {
  final String placeId;
  final GooglePlace googlePlace;

  const DetailsPage({Key? key, required this.placeId, required this.googlePlace})
      : super(key: key);

  [@override](/user/override)
  _DetailsPageState createState() => _DetailsPageState(this.placeId, this.googlePlace);
}

class _DetailsPageState extends State<DetailsPage> {
  final String placeId;
  final GooglePlace googlePlace;
  DetailsResult? detailsResult;
  List<Uint8List> images = [];

  _DetailsPageState(this.placeId, this.googlePlace);

  [@override](/user/override)
  void initState() {
    getDetails(this.placeId);
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Details"), backgroundColor: Colors.blueAccent),
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.blueAccent,
        onPressed: () {
          getDetails(this.placeId);
        },
        child: Icon(Icons.refresh),
      ),
      body: SafeArea(
        child: Container(
          margin: EdgeInsets.only(right: 20, left: 20, top: 20),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Container(
                height: 200,
                child: ListView.builder(
                  scrollDirection: Axis.horizontal,
                  itemCount: images.length,
                  itemBuilder: (context, index) {
                    return Container(
                      width: 250,
                      child: Card(
                        elevation: 4,
                        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
                        child: ClipRRect(
                          borderRadius: BorderRadius.circular(10.0),
                          child: Image.memory(images[index], fit: BoxFit.fill),
                        ),
                      ),
                    );
                  },
                ),
              ),
              SizedBox(height: 10),
              Expanded(
                child: Card(
                  elevation: 4,
                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
                  child: ListView(
                    children: <Widget>[
                      Container(
                        margin: EdgeInsets.only(left: 15, top: 10),
                        child: Text("Details", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
                      ),
                      detailsResult?.types != null
                          ? Container(
                              margin: EdgeInsets.only(left: 15, top: 10),
                              height: 50,
                              child: ListView.builder(
                                scrollDirection: Axis.horizontal,
                                itemCount: detailsResult!.types!.length,
                                itemBuilder: (context, index) {
                                  return Container(
                                    margin: EdgeInsets.only(right: 10),
                                    child: Chip(
                                      label: Text(detailsResult!.types![index], style: TextStyle(color: Colors.white)),
                                      backgroundColor: Colors.blueAccent,
                                    ),
                                  );
                                },
                              ),
                            )
                          : Container(),
                      Container(
                        margin: EdgeInsets.only(left: 15, top: 10),
                        child: ListTile(
                          leading: CircleAvatar(child: Icon(Icons.location_on)),
                          title: Text('Address: ${detailsResult?.formattedAddress ?? "null"}'),
                        ),
                      ),
                      Container(
                        margin: EdgeInsets.only(left: 15, top: 10),
                        child: ListTile(
                          leading: CircleAvatar(child: Icon(Icons.location_searching)),
                          title: Text('Geometry: ${detailsResult?.geometry?.location?.lat}, ${detailsResult?.geometry?.location?.lng}'),
                        ),
                      ),
                      Container(
                        margin: EdgeInsets.only(left: 15, top: 10),
                        child: ListTile(
                          leading: CircleAvatar(child: Icon(Icons.timelapse)),
                          title: Text('UTC offset: ${detailsResult?.utcOffset ?? "null"} min'),
                        ),
                      ),
                      Container(
                        margin: EdgeInsets.only(left: 15, top: 10),
                        child: ListTile(
                          leading: CircleAvatar(child: Icon(Icons.rate_review)),
                          title: Text('Rating: ${detailsResult?.rating ?? "null"}'),
                        ),
                      ),
                      Container(
                        margin: EdgeInsets.only(left: 15, top: 10),
                        child: ListTile(
                          leading: CircleAvatar(child: Icon(Icons.attach_money)),
                          title: Text('Price level: ${detailsResult?.priceLevel ?? "null"}'),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              Container(margin: EdgeInsets.only(top: 20, bottom: 10), child: Image.asset("assets/powered_by_google.png")),
            ],
          ),
        ),
      ),
    );
  }

  void getDetails(String placeId) async {
    var result = await this.googlePlace.details.get(placeId);
    if (result != null && result.result != null && mounted) {
      setState(() {
        detailsResult = result.result;
        images = [];
      });

      if (result.result?.photos != null) {
        for (var photo in result.result!.photos!) {
          getPhoto(photo.photoReference!);
        }
      }
    }
  }

  void getPhoto(String photoReference) async {
    var result = await this.googlePlace.photos.get(photoReference: photoReference, maxWidth: 400);
    if (result != null && mounted) {
      setState(() {
        images.add(result);
      });
    }
  }
}

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

1 回复

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


当然,以下是一个关于如何在Flutter项目中使用google_place_plus插件来获取地点信息的代码示例。这个插件是google_maps_place_picker的一个替代或增强版,用于在Flutter应用中集成谷歌地点服务。

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

dependencies:
  flutter:
    sdk: flutter
  google_place_plus: ^latest_version  # 请替换为最新版本号

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

接下来,你需要在Android和iOS项目中配置API密钥和必要的权限。这通常涉及到在android/app/src/main/AndroidManifest.xml中添加位置权限,以及在iOS的Info.plist中添加相应的键。

Android配置

AndroidManifest.xml中添加以下权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

确保你的API密钥已经在android/app/build.gradle文件中的manifestPlaceholders里设置:

android {
    ...
    defaultConfig {
        ...
        manifestPlaceholders = [
                'google_maps_android_api_key':'YOUR_ANDROID_API_KEY'
        ]
    }
}

iOS配置

Info.plist中添加以下键:

<key>NSLocationWhenInUseUsageDescription</key>
<string>需要您的位置信息来搜索地点</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>需要您的位置信息来搜索地点</string>
<key>GOOGLE_API_KEY</key>
<string>YOUR_IOS_API_KEY</string>

注意:虽然GOOGLE_API_KEY在iOS的Info.plist中不是必需的(因为通常我们在iOS项目中通过其他方式管理API密钥,比如使用GoogleService-Info.plist),但确保你的API密钥在项目中正确配置是很重要的。

Flutter代码示例

下面是一个简单的Flutter应用示例,展示了如何使用google_place_plus插件来搜索并显示地点信息:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Google Place Plus Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PlacePickerDemo(),
    );
  }
}

class PlacePickerDemo extends StatefulWidget {
  @override
  _PlacePickerDemoState createState() => _PlacePickerDemoState();
}

class _PlacePickerDemoState extends State<PlacePickerDemo> {
  PlaceResult? selectedPlace;

  Future<void> _pickPlace() async {
    final PlacePickerResult result = await PlacePicker.show(
      context: context,
      apiKey: 'YOUR_API_KEY', // 在这里替换为你的API密钥
      mode: Mode.overlay,
    );

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Google Place Plus Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: _pickPlace,
              child: Text('Pick a Place'),
            ),
            if (selectedPlace != null)
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Text('Name: ${selectedPlace!.name}'),
                    Text('Address: ${selectedPlace!.formattedAddress}'),
                    Text('Rating: ${selectedPlace!.rating?.toString()}'),
                    // 根据需要添加更多地点信息
                  ],
                ),
              ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个按钮,点击该按钮将打开一个地点选择器。用户可以选择一个地点,然后应用将显示所选地点的名称、地址和评分等信息。

请确保替换YOUR_API_KEY为你的实际API密钥,并且在发布应用之前检查并遵守谷歌API的使用条款和条件。

回到顶部