Flutter地点搜索插件flutter_google_places的使用

Flutter地点搜索插件flutter_google_places的使用

简介

flutter_google_places 是一个用于Flutter应用的Google Places自动补全插件。它可以帮助用户在应用中快速查找和选择地点。以下是关于如何使用该插件的详细说明和完整示例代码。

快速开始

  1. 添加依赖pubspec.yaml 文件中添加 flutter_google_places 依赖,并确保使用最新版本:

    dependencies:
      flutter:
        sdk: flutter
      flutter_google_places: ^<last-version>
    
  2. 导入必要的包 在你的 Dart 文件中导入 flutter_google_placesgoogle_maps_webservice 包:

    import 'package:flutter/material.dart';
    import 'package:flutter_google_places/flutter_google_places.dart';
    import 'package:google_maps_webservice/places.dart';
    import 'package:google_api_headers/google_api_headers.dart';
    
  3. 设置API密钥 你需要一个有效的 Google Places API 密钥。将其存储在一个常量中,例如:

    const kGoogleApiKey = "YOUR_API_KEY";
    
  4. 创建主应用程序 下面是一个完整的示例代码,展示了如何使用 flutter_google_places 插件来实现地点搜索功能:

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

import 'package:flutter/material.dart';
import 'package:flutter_google_places/flutter_google_places.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:google_api_headers/google_api_headers.dart';

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

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

class RoutesWidget extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) => MaterialApp(
        title: "My App",
        theme: customTheme,
        routes: {
          "/": (_) => MyApp(),
          "/search": (_) => CustomSearchScaffold(),
        },
      );
}

class MyApp extends StatefulWidget {
  [@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: Text("My App"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _buildDropdownMenu(),
            ElevatedButton(
              onPressed: _handlePressButton,
              child: Text("搜索地点"),
            ),
            ElevatedButton(
              child: Text("自定义"),
              onPressed: () {
                Navigator.of(context).pushNamed("/search");
              },
            ),
          ],
        ),
      ),
    );
  }

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

  void onError(PlacesAutocompleteResponse response) {
    homeScaffoldKey.currentState.showSnackBar(
      SnackBar(content: Text(response.errorMessage)),
    );
  }

  Future<void> _handlePressButton() async {
    // 显示输入自动补全并选择模式
    // 然后获取选中的 Prediction
    Prediction p = await PlacesAutocomplete.show(
      context: context,
      apiKey: kGoogleApiKey,
      onError: onError,
      mode: _mode,
      language: "zh", // 设置为中文
      decoration: InputDecoration(
        hintText: '搜索',
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(20),
          borderSide: BorderSide(
            color: Colors.white,
          ),
        ),
      ),
      components: [Component(Component.country, "cn")], // 限制为中国
    );

    displayPrediction(p, homeScaffoldKey.currentState);
  }
}

Future<Null> displayPrediction(Prediction p, ScaffoldState scaffold) async {
  if (p != null) {
    // 获取详细信息(经纬度)
    GoogleMapsPlaces _places = GoogleMapsPlaces(
      apiKey: kGoogleApiKey,
      apiHeaders: await GoogleApiHeaders().getHeaders(),
    );
    PlacesDetailsResponse detail = await _places.getDetailsByPlaceId(p.placeId);
    final lat = detail.result.geometry.location.lat;
    final lng = detail.result.geometry.location.lng;

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

// 自定义搜索页面
// 你的小部件需要继承 [GooglePlacesAutocompleteWidget]
// 并且状态类需要继承 [GooglePlacesAutocompleteState]
class CustomSearchScaffold extends PlacesAutocompleteWidget {
  CustomSearchScaffold()
      : super(
          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: AppBarPlacesAutoCompleteTextField());
    final body = PlacesAutocompleteResult(
      onTap: (p) {
        displayPrediction(p, searchScaffoldKey.currentState);
      },
      logo: Row(
        children: [FlutterLogo()],
        mainAxisAlignment: MainAxisAlignment.center,
      ),
    );
    return Scaffold(key: searchScaffoldKey, appBar: appBar, body: body);
  }

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

  [@override](/user/override)
  void onResponse(PlacesAutocompleteResponse response) {
    super.onResponse(response);
    if (response != null && response.predictions.isNotEmpty) {
      searchScaffoldKey.currentState.showSnackBar(
        SnackBar(content: Text("获取到结果")),
      );
    }
  }
}

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地点搜索插件flutter_google_places的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


当然,以下是一个关于如何在Flutter应用中使用flutter_google_places插件进行地点搜索的示例代码。这个插件允许你集成Google Places API到你的Flutter应用中,从而进行地点搜索、自动完成等功能。

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

dependencies:
  flutter:
    sdk: flutter
  flutter_google_places: ^0.3.0  # 请注意版本号,根据实际情况更新

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

接下来,你需要设置Google Places API。为此,你需要一个Google Cloud项目,并启用Places API。你还需要获取一个API密钥,并在你的Flutter应用中配置它。

在你的Flutter项目的android/app/src/main/AndroidManifest.xml文件中添加API密钥(注意,出于安全考虑,最好不要在客户端代码中硬编码API密钥,这里只是为了演示):

<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="YOUR_API_KEY"/>

在iOS项目中,你需要在Info.plist中添加一个键NSAppTransportSecurity,允许你的应用访问Google的API,并且你需要在你的iOS项目的App Delegate中设置API密钥(同样,出于安全考虑,不推荐这样做,可以使用更安全的存储方式,如Keychain)。

现在,让我们编写一些Flutter代码来使用flutter_google_places插件。

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

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

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

class PlaceSearchDemo extends StatefulWidget {
  @override
  _PlaceSearchDemoState createState() => _PlaceSearchDemoState();
}

class _PlaceSearchDemoState extends State<PlaceSearchDemo> {
  GooglePlaces _places = GooglePlaces(apiKey: 'YOUR_API_KEY');
  Prediction? _prediction;

  Future<void> _handleSearch(String query) async {
    setState(() {
      _prediction = null;
    });
    PredictionResult predictions = await _places.autocomplete.get(query);

    if (predictions.status == 'OK') {
      setState(() {
        _prediction = predictions.predictions!.first;
      });
    }
  }

  Future<void> _getPlaceDetails() async {
    if (_prediction != null) {
      PlaceDetailsResult place = await _places.details.get(_prediction!.placeId!);

      if (place.status == 'OK') {
        print('Place name: ${place.result!.name}');
        print('Place address: ${place.result!.formattedAddress}');
        // 你可以在这里处理获取到的地点详情,比如显示在一个Text控件中
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          TextField(
            decoration: InputDecoration(
              labelText: 'Search for a place',
              suffixIcon: IconButton(
                icon: Icon(Icons.search),
                onPressed: () {
                  final String query = (context as _TextEditingContext).textEditingValue.text;
                  _handleSearch(query);
                },
              ),
            ),
            onChanged: (value) {
              // 实时搜索建议
              _handleSearch(value);
            },
          ),
          if (_prediction != null)
            Padding(
              padding: const EdgeInsets.symmetric(vertical: 8.0),
              child: ListTile(
                leading: Icon(Icons.location_on),
                title: Text(_prediction!.description!),
                subtitle: Text(_prediction!.placeId!),
                onTap: () {
                  _getPlaceDetails();
                },
              ),
            ),
        ],
      ),
    );
  }
}

// 注意:由于TextField的onChanged回调中不能直接访问BuildContext,
// 你可能需要使用其他方式(如TextEditingController)来管理文本输入状态,
// 这里为了简化示例代码,直接展示了调用方式,实际使用时可能需要调整。

这个示例展示了如何使用flutter_google_places插件进行地点搜索和获取地点详情。注意,出于安全考虑,你应该避免在客户端代码中硬编码API密钥,并考虑使用更安全的方法来存储和访问它。此外,处理用户输入和搜索建议时,你可能需要添加更多的错误处理和用户体验优化。

回到顶部