如何在 Flutter 中获取 Google 地图 OpeningHoursDetail 和搜索功能结果

问题描述 投票:0回答:1

我目前正在研究一些要包含在 Flutter 应用程序中的 Google 地图功能。我尝试了几天来获取 OpeningHoursDetail(开放/关闭、日期和时间)并显示为文本,但它没有显示任何结果或显示任何错误。有人知道如何解决这个问题并获取详细信息吗?第二个问题是搜索功能,我在输入地址或地名后无法获得他们的自动完成推荐或搜索结果,哪一部分出了问题?我已经启用 Places and Maps SDK for API,包括 Android XML 文件中的所有权限。任何人都可以指出或修改代码来为我展示解决方案,感谢这些帮助,谢谢。

代码:

import 'package:flutter/material.dart';
import 'package:flutter_google_places/flutter_google_places.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:location/location.dart' as LocationManager;

const kGoogleApiKey = "REPLACE_WITH_YOUR_OWN_API_KEY";
GoogleMapsPlaces _places = GoogleMapsPlaces(apiKey: kGoogleApiKey);

class GoogleMapView extends StatefulWidget {
  const GoogleMapView({super.key});

  @override
  State<GoogleMapView> createState() => _GoogleMapViewState();
}

class _GoogleMapViewState extends State<GoogleMapView> {
  late GoogleMapController mapController;
  List<PlacesSearchResult> places = [];
  bool isLoading = false;
  String? errorMessage;
  late TextEditingController _searchController;

  @override
  void initState() {
    super.initState();
    _searchController = TextEditingController();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Nearby Places"),
        actions: [
          IconButton(
            onPressed: _searchPlaces,
            icon: const Icon(Icons.search),
          ),
        ],
      ),
      body: Stack(
        children: [
          GoogleMap(
            onMapCreated: _onMapCreated,
            initialCameraPosition: const CameraPosition(
              target: LatLng(0, 0), // Default: Center of the world
              zoom: 12,
            ),
            markers: _buildMarkers(),
          ),
          if (isLoading)
            const Center(
              child: CircularProgressIndicator(),
            ),
          if (errorMessage != null)
            Center(
              child: Text(errorMessage!),
            ),
        ],
      ),
    );
  }

  Set<Marker> _buildMarkers() {
    return places.map((place) {
      return Marker(
        markerId: MarkerId(place.placeId),
        position:
            LatLng(place.geometry!.location.lat, place.geometry!.location.lng),
        onTap: () {
          _showPlaceDetails(place);
        },
      );
    }).toSet();
  }

  void _onMapCreated(GoogleMapController controller) {
    setState(() {
      mapController = controller;
      _getCurrentLocation();
    });
  }

  Future<void> _getCurrentLocation() async {
    try {
      LocationManager.LocationData? currentLocation =
          await LocationManager.Location().getLocation();
      LatLng userLocation =
          LatLng(currentLocation.latitude!, currentLocation.longitude!);
      mapController.animateCamera(CameraUpdate.newLatLngZoom(userLocation, 12));
      _getNearbyPlaces(userLocation);
    } catch (e) {
      setState(() {
        errorMessage = "Error getting current location";
      });
    }
  }

  Future<void> _getNearbyPlaces(LatLng userLocation) async {
    setState(() {
      isLoading = true;
      errorMessage = null;
    });

    final location =
        Location(lat: userLocation.latitude, lng: userLocation.longitude);
    final result = await _places.searchNearbyWithRadius(
      location,
      5000, // Radius in meters, adjust as needed
      type: "restaurant", // Specify the desired place type
    );

    setState(() {
      isLoading = false;
      if (result.status == "OK") {
        places = result.results;
      } else {
        errorMessage = result.errorMessage;
      }
    });
  }

Future<void> _searchPlaces() async {
    Prediction? prediction = await PlacesAutocomplete.show(
      context: context,
      apiKey: kGoogleApiKey,
      mode: Mode.overlay,
      components: [
        Component(Component.country, "MY"),
        Component(Component.country, "SG")
      ],
    );

    if (prediction != null) {
      PlacesDetailsResponse details =
          await _places.getDetailsByPlaceId(prediction.placeId!);
      PlaceDetails placeDetails = details.result;
      LatLng location = LatLng(
        placeDetails.geometry!.location.lat,
        placeDetails.geometry!.location.lng,
      );
      mapController.animateCamera(CameraUpdate.newLatLngZoom(location, 15));
      _getNearbyPlaces(location);
    }
  }


  void _showPlaceDetails(PlacesSearchResult place) {
    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) {
        return Container(
          height: 350,
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              Text(
                place.name,
                style: const TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: 20.0,
                ),
              ),
              const SizedBox(height: 8.0),
              Text(place.vicinity!),
              const SizedBox(height: 8.0),
              if (place.openingHours != null &&
                  place.openingHours!.periods != null)
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Operation Hours:',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 4.0),
                    for (var period in place.openingHours!.periods)
                      Text(
                        '${_getDayString(period.open!.day)}: ${_formatTime(period.open!.time as TimeOfDay)} - ${_formatTime(period.close!.time as TimeOfDay)}',
                      ),
                  ],
                ),
            ],
          ),
        );
      },
    );
  }

  String _getDayString(int day) {
    switch (day) {
      case DateTime.monday:
        return 'Monday';
      case DateTime.tuesday:
        return 'Tuesday';
      case DateTime.wednesday:
        return 'Wednesday';
      case DateTime.thursday:
        return 'Thursday';
      case DateTime.friday:
        return 'Friday';
      case DateTime.saturday:
        return 'Saturday';
      case DateTime.sunday:
        return 'Sunday';
      default:
        return '';
    }
  }

  String _formatTime(TimeOfDay time) {
    return '${time.hour}:${time.minute.toString().padLeft(2, '0')}';
  }
}
flutter dart google-maps google-cloud-platform
1个回答
0
投票

从您提供的代码中,我注意到您还有其他各种目标,例如获取附近的地点和处理用户的当前位置。由于您的问题具体是关于自动完成功能不起作用和显示开放时间的,因此我专注于这些部分并成功创建了一个解决这些问题的 Flutter 应用程序。

以下是代码关键部分的细分:

我的 UI 中有一个 “搜索地点” 按钮,按下该按钮会触发名为

_handlePressButton
的方法。

@override
      Widget build(BuildContext context) {
        return Scaffold(
          key: homeScaffoldKey,
          appBar: AppBar(
            title: const Text("Google Maps Search"),
          ),
          body: Stack(
            children: [
              GoogleMap(
                initialCameraPosition: initialCameraPosition,
                markers: markersList,
                mapType: MapType.normal,
                onMapCreated: (GoogleMapController controller) {
                  mapController = controller;
                },
              ),
              ElevatedButton(
                  onPressed: _handlePressButton, child: const Text("Search Places"))
            ],
          ),
        );
      }

_handlePressButton
方法中,就像您所做的那样,使用
PlacesAutocomplete.show()
方法来显示搜索界面。一旦用户从自动完成结果中选择了一个位置,就会调用
displayPrediction
方法。

Future<void> _handlePressButton() async {
        Prediction? p = await PlacesAutocomplete.show(
            context: context,
            apiKey: kGoogleApiKey,
            onError: onError,
            mode: _mode,
            language: "en",
            strictbounds: false,
            types: [""],
            decoration: InputDecoration(
                hintText: "Search Places",
                focusedBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: BorderSide(color: Colors.green),
                )),
            components: [
              Component(Component.country, "us"),
              Component(Component.country, "ph")
            ]);
        displayPrediction(p!, homeScaffoldKey.currentState);
      }

displayPrediction
方法使用
GoogleMapsPlaces
包中的
google_maps_webservice
类来获取详细信息,例如位置的 lat、lng 和 openingHours。这些详细信息是使用
getDetailsByPlaceId
方法获取的,并将其显示在
AlertDialog
中。

我没有像您的

_getDayString
方法那样尝试手动提取每日开放时间,而是使用了
openingHours.weedayText
(这是代表一周中每一天的开放时间的字符串列表) 属性放置 API 响应。

Future<void> displayPrediction(
          Prediction p, ScaffoldState? currentState) async {
        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;
        final openingHoursResult =
            detail.result.openingHours?.weekdayText.join("\n");
    
        showDialog(
            context: context,
            builder: (context) {
              return AlertDialog(
                title: Text(detail.result.name),
                content: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Align(
                      alignment: Alignment.centerLeft,
                      child: Text("Address: $lat, $lng"),
                    ),
                    Align(
                      alignment: Alignment.centerLeft,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text("Opening Hours:"),
                          Text(openingHoursResult ?? "Not available"),
                        ],
                      ),
                    ),
                  ],
                ),
              );
            });

有了这个,事情似乎进展顺利。

如果您想尝试的话,这是我的完整代码。

main.dart

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const GoogleMapsSearch(title: 'Flutter Demo Home Page'),
    );
  }
}

class GoogleMapsSearch extends StatefulWidget {
  const GoogleMapsSearch({super.key, required String title});

  @override
  State<GoogleMapsSearch> createState() => _GoogleMapsSearchState();
}

const kGoogleApiKey = 'YOUR_API_KEY';

final homeScaffoldKey = GlobalKey<ScaffoldState>();

class _GoogleMapsSearchState extends State<GoogleMapsSearch> {
  static const CameraPosition initialCameraPosition = CameraPosition(
    target: LatLng(37.42796, -122.08574),
    zoom: 14.0,
  );

  Set<Marker> markersList = {};

  late GoogleMapController mapController;

  final Mode _mode = Mode.overlay;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: homeScaffoldKey,
      appBar: AppBar(
        title: const Text("Google Maps Search"),
      ),
      body: Stack(
        children: [
          GoogleMap(
            initialCameraPosition: initialCameraPosition,
            markers: markersList,
            mapType: MapType.normal,
            onMapCreated: (GoogleMapController controller) {
              mapController = controller;
            },
          ),
          ElevatedButton(
              onPressed: _handlePressButton, child: const Text("Search Places"))
        ],
      ),
    );
  }

  Future<void> _handlePressButton() async {
    Prediction? p = await PlacesAutocomplete.show(
        context: context,
        apiKey: kGoogleApiKey,
        onError: onError,
        mode: _mode,
        language: "en",
        strictbounds: false,
        types: [""],
        decoration: InputDecoration(
            hintText: "Search Places",
            focusedBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(20),
              borderSide: BorderSide(color: Colors.green),
            )),
        components: [
          Component(Component.country, "us"),
          Component(Component.country, "ph")
        ]);
    displayPrediction(p!, homeScaffoldKey.currentState);
  }

  void onError(PlacesAutocompleteResponse response) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      elevation: 0,
      behavior: SnackBarBehavior.floating,
      backgroundColor: Colors.transparent,
      content: AwesomeSnackbarContent(
        title: 'Message',
        message: response.errorMessage!,
        contentType: ContentType.failure,
      ),
    ));
  }

  Future<void> displayPrediction(
      Prediction p, ScaffoldState? currentState) async {
    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;
    final openingHoursResult =
        detail.result.openingHours?.weekdayText.join("\n");

    showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text(detail.result.name),
            content: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Align(
                  alignment: Alignment.centerLeft,
                  child: Text("Address: $lat, $lng"),
                ),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text("Opening Hours:"),
                      Text(openingHoursResult ?? "Not available"),
                    ],
                  ),
                ),
              ],
            ),
          );
        });

    markersList.clear();
    markersList.add(Marker(
        markerId: const MarkerId("0"),
        position: LatLng(lat, lng),
        infoWindow: InfoWindow(
          title: detail.result.name,
        )));

    setState(() {});

    mapController
        .animateCamera(CameraUpdate.newLatLngZoom(LatLng(lat, lng), 14.0));
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.