Flutterで現在地から1km以内のコンビニを取得する

## はじめに
表題通り、GoogleMapAPIで現在地から1km以内のコンビニを取得する。

## 成果物
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/64142805-17f5-8e4b-f152-f3139e68a7b4.png"/>
→1km以内のコンビニにマップを設定しています。

### ソースコード
```dart:lib/main.dart
import 'package:flutter/material.dart';
import 'screens/nearby_places_screen.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: NearbyPlacesScreen(),
    );
  }
}
```

```dart:lib/screens/nearby_places_screen.dart
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../services/location_service.dart';
import '../services/places_service.dart';
import '../models/place.dart';

class NearbyPlacesScreen extends StatefulWidget {
  @override
  _NearbyPlacesScreenState createState() => _NearbyPlacesScreenState();
}

class _NearbyPlacesScreenState extends State<NearbyPlacesScreen> {
  List<Place> _places = [];
  bool _loading = true;
  String _error = '';
  GoogleMapController? _mapController;
  Set<Marker> _markers = {};
  LatLng _initialPosition = LatLng(35.6895, 139.6917); // 東京の緯度経度をデフォルトに設定

  @override
  void initState() {
    super.initState();
    _loadPlaces();
  }

  Future<void> _loadPlaces() async {
    try {
      final position = await LocationService.getCurrentLocation();
      _initialPosition = LatLng(position.latitude, position.longitude);
      final places = await PlacesService.getNearbyPlaces(
          position.latitude, position.longitude);
      setState(() {
        _places = places;
        _markers.add(
          Marker(
            markerId: MarkerId('currentLocation'),
            position: _initialPosition,
            infoWindow: InfoWindow(title: 'Current Location'),
          ),
        );
        for (var place in places) {
          _markers.add(
            Marker(
              markerId: MarkerId(place.name),
              position: LatLng(place.lat, place.lng),
              infoWindow: InfoWindow(title: place.name),
            ),
          );
        }
        _loading = false;
      });
    } catch (e) {
      setState(() {
        _error = e.toString();
        _loading = false;
      });
    }
  }

  void _onMapCreated(GoogleMapController controller) {
    _mapController = controller;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Nearby Convenience Stores'),
      ),
      body: _loading
          ? Center(child: CircularProgressIndicator())
          : _error.isNotEmpty
              ? Center(child: Text('Error: $_error'))
              : GoogleMap(
                  onMapCreated: _onMapCreated,
                  initialCameraPosition: CameraPosition(
                    target: _initialPosition,
                    zoom: 14.0,
                  ),
                  markers: _markers,
                ),
    );
  }
}
```

```dart:lib/services/location_service.dart
import 'package:geolocator/geolocator.dart';

class LocationService {
  static Future<Position> getCurrentLocation() async {
    bool serviceEnabled;
    LocationPermission permission;

    // Location services are not enabled don't continue
    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return Future.error('Location services are disabled.');
    }

    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        return Future.error('Location permissions are denied');
      }
    }

    if (permission == LocationPermission.deniedForever) {
      return Future.error(
          'Location permissions are permanently denied, we cannot request permissions.');
    }

    Position position = await Geolocator.getCurrentPosition();
    print('Current position: ${position.latitude}, ${position.longitude}');
    return position;
  }
}
```

```dart:lib/services/places_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/place.dart';

class PlacesService {
  static final String _apiKey = 'XXXXXXXXXXX';

  static Future<List<Place>> getNearbyPlaces(double lat, double lng) async {
    final String url =
        'https://maps.googleapis.com/maps/api/place/nearbysearch/json'
        '?location=$lat,$lng'
        '&radius=1000'
        '&type=convenience_store'
        '&key=$_apiKey';

    final response = await http.get(Uri.parse(url));

    if (response.statusCode == 200) {
      final data = json.decode(response.body);
      print('API Response: $data'); // APIレスポンスをログ出力
      final results = data['results'] as List;
      return results.map((result) => Place.fromJson(result)).toList();
    } else {
      throw Exception('Failed to load nearby places');
    }
  }
}

```

```dart:lib/models/place.dart
class Place {
  final String name;
  final double lat;
  final double lng;

  Place({required this.name, required this.lat, required this.lng});

  factory Place.fromJson(Map<String, dynamic> json) {
    return Place(
      name: json['name'],
      lat: json['geometry']['location']['lat'],
      lng: json['geometry']['location']['lng'],
    );
  }
}
```

```xml:Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- 既存の設定項目 -->

    <!-- 位置情報の使用許可の説明を追加 -->
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>現在地を取得して近くのコンビニを検索するために、位置情報の使用を許可してください。</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>現在地を取得して近くのコンビニを検索するために、常時位置情報の使用を許可してください。</string>

    <!-- 既存の設定項目 -->
</dict>
</plist>
```