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

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

fcts_flutter_google_places 库为 Flutter 提供了无缝集成的 Google 地点自动完成小部件,使开发人员能够轻松地将位置搜索和建议功能整合到他们的应用程序中。该库基于强大的 flutter_google_maps_webservice 库构建,遵循 Google 地图网络服务的官方文档以确保准确性和可靠性。

此包由 Forwardcode Techstudio 维护,这是一家值得信赖的软件解决方案提供商,确保高质量的代码和定期更新。今天就通过直观且用户友好的 Google 地点自动完成功能来增强您的 Flutter 应用程序吧。

根据 Stack Overflow,即使您仅使用免费配额,也需要在帐户上启用结算功能。

安装

pubspec.yaml 文件的 dependencies: 部分添加以下行:

dependencies:
  fcts_flutter_google_places: <latest_version>

使用

const kGoogleApiKey = "API_KEY";

Prediction p = await PlacesAutocomplete.show(
                          context: context,
                          apiKey: kGoogleApiKey,
                          mode: Mode.overlay, // Mode.fullscreen
                          language: "fr",
                          components: [new Component(Component.country, "fr")]);

示例应用

查看 example 目录下的 Flutter 应用。

贡献者

  1. Rahul Gupta
  2. Forwardcode Techstudio

许可证

MIT


以下是完整的示例代码:

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

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

const kGoogleApiKey = "YOUR_API_KEY";

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: "en",
      themeData: Theme.of(context).copyWith(
        appBarTheme: AppBarTheme(
            backgroundColor: Color.fromRGBO(32, 32, 32, 1),
            elevation: 0,
            iconTheme: IconThemeData(color: Colors.green)),
      ),
      decoration: InputDecoration(
        hintText: 'Search',
        enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(12),
          borderSide: const BorderSide(
            color: Color(0XFF3C3C3C),
          ),
        ),
        prefixIcon: Icon(Icons.search),
        suffixIcon: Icon(Icons.delete),
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(12),
          borderSide: const BorderSide(
            color: Colors.grey,
          ),
        ),
      ),
      components: [Component(Component.country, "in")],
      predictionLeadingIcon: Icon(Icons.abc), // 可以是任何小部件
    );

    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")),
    );
  }
}

// 自定义搜索小部件
// 基本上您的小部件需要扩展 [GooglePlacesAutocompleteWidget]
// 并且您的状态需要扩展 [GooglePlacesAutocompleteState]
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() {
    // 生成 xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx / 8-4-4-4-12。
    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谷歌地点搜索插件fcts_flutter_google_places的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


fcts_flutter_google_places 是一个用于在 Flutter 应用中集成 Google Places API 的插件。它可以帮助你在应用中实现地点搜索、自动完成、地点详情获取等功能。以下是如何使用该插件的基本步骤:

1. 安装插件

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

dependencies:
  flutter:
    sdk: flutter
  fcts_flutter_google_places: ^最新版本

然后运行 flutter pub get 来安装插件。

2. 获取 Google Places API 密钥

你需要一个有效的 Google Places API 密钥来使用这个插件。你可以在 Google Cloud Console 中创建一个项目并启用 Places API,然后生成一个 API 密钥。

3. 初始化插件

在你的应用启动时,需要初始化 fcts_flutter_google_places 插件,并设置你的 Google Places API 密钥。

import 'package:fcts_flutter_google_places/fcts_flutter_google_places.dart';

void main() {
  FctsFlutterGooglePlaces.initialize(apiKey: 'YOUR_GOOGLE_PLACES_API_KEY');
  runApp(MyApp());
}

4. 实现地点搜索

你可以使用 FctsFlutterGooglePlaces 提供的 Autocomplete 小部件来实现地点搜索功能。

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

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

class _PlaceSearchScreenState extends State<PlaceSearchScreen> {
  String _selectedPlaceId;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Place Search'),
      ),
      body: Column(
        children: [
          FctsGooglePlacesAutocomplete(
            apiKey: 'YOUR_GOOGLE_PLACES_API_KEY',
            onSelected: (Place place) {
              setState(() {
                _selectedPlaceId = place.placeId;
              });
            },
          ),
          if (_selectedPlaceId != null)
            FutureBuilder<PlaceDetails>(
              future: FctsFlutterGooglePlaces.getPlaceDetails(_selectedPlaceId),
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return CircularProgressIndicator();
                } else if (snapshot.hasError) {
                  return Text('Error: ${snapshot.error}');
                } else if (!snapshot.hasData) {
                  return Text('No data found');
                } else {
                  final placeDetails = snapshot.data;
                  return Column(
                    children: [
                      Text('Place Name: ${placeDetails.name}'),
                      Text('Address: ${placeDetails.formattedAddress}'),
                      Text('Latitude: ${placeDetails.geometry.location.lat}'),
                      Text('Longitude: ${placeDetails.geometry.location.lng}'),
                    ],
                  );
                }
              },
            ),
        ],
      ),
    );
  }
}

5. 处理地点详情

在上面的代码中,当用户选择一个地点后,你可以使用 FctsFlutterGooglePlaces.getPlaceDetails 方法来获取该地点的详细信息。

6. 处理权限和错误

在使用 Google Places API 时,确保你有正确的权限,并且处理可能出现的错误(例如网络错误、API 限制等)。

7. 发布应用

在发布应用时,确保你使用的是生产环境的 API 密钥,并且遵循 Google 的 API 使用政策和限制。

8. 进一步定制

根据你的需求,你可以进一步定制 FctsGooglePlacesAutocomplete 小部件的外观和行为,例如设置初始值、限制搜索范围、自定义结果项的显示等。

示例代码

以下是一个完整的示例代码:

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

void main() {
  FctsFlutterGooglePlaces.initialize(apiKey: 'YOUR_GOOGLE_PLACES_API_KEY');
  runApp(MyApp());
}

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

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

class _PlaceSearchScreenState extends State<PlaceSearchScreen> {
  String _selectedPlaceId;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Place Search'),
      ),
      body: Column(
        children: [
          FctsGooglePlacesAutocomplete(
            apiKey: 'YOUR_GOOGLE_PLACES_API_KEY',
            onSelected: (Place place) {
              setState(() {
                _selectedPlaceId = place.placeId;
              });
            },
          ),
          if (_selectedPlaceId != null)
            FutureBuilder<PlaceDetails>(
              future: FctsFlutterGooglePlaces.getPlaceDetails(_selectedPlaceId),
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return CircularProgressIndicator();
                } else if (snapshot.hasError) {
                  return Text('Error: ${snapshot.error}');
                } else if (!snapshot.hasData) {
                  return Text('No data found');
                } else {
                  final placeDetails = snapshot.data;
                  return Column(
                    children: [
                      Text('Place Name: ${placeDetails.name}'),
                      Text('Address: ${placeDetails.formattedAddress}'),
                      Text('Latitude: ${placeDetails.geometry.location.lat}'),
                      Text('Longitude: ${placeDetails.geometry.location.lng}'),
                    ],
                  );
                }
              },
            ),
        ],
      ),
    );
  }
}
回到顶部