😇

Flutter + Google MapAPI + Firebase

2024/07/05に公開

対象者

  • Firebaseを使ったことある
  • GoogleMapAPIを使ったことある

わかっているの前提で記事を書くので、GoogleMapAPIやFirebaseの設定はご自分でされてください。

プロジェクトの説明

今回は、登録した地域のお店の位置情報を保存して、Flutterアプリに表示するのをやってみようと思います。Cloud Firestoreに保存したマスターデータを使うので、ご自分の保存してみたい位置情報を登録してみてください。

[このようになっております]

SwiftUIで作ったアプリとDB設計は一緒なので、こちらもよろしければ参考にしてみてください。
https://zenn.dev/joo_hashi/articles/a38b369511970b

コレクションはの構造は、以下のようになっております。

/shop
|
-----shop_name: string
|
-----address: string
|
-----location: geopoint

コレクションIDが、shopで、shop_nameaddressだけ、string型で、locationは、緯度・経度が保存できるgeopoint型を使用しております。

今回使用したデモ用アプリのコードはこちらです:

main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MapScreen(),
    );
  }
}

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

  
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  late GoogleMapController mapController;
  final Set<Marker> _markers = {};

  // Firestoreのインスタンスを取得
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  // shopコレクションの参照を取得するメソッド
  CollectionReference get shopCollection => _firestore.collection('shop');

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

  // shopコレクションからデータを取得するメソッド
  Future<List<Shop>> getShops() async {
    QuerySnapshot querySnapshot = await shopCollection.get();
    return querySnapshot.docs.map((doc) => Shop.fromFirestore(doc)).toList();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Shop Locations'),
        backgroundColor: Colors.green[700],
      ),
      body: FutureBuilder<List<Shop>>(
        future: getShops(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          }
          if (!snapshot.hasData || snapshot.data!.isEmpty) {
            return const Center(child: Text('No shops found'));
          }

          _markers.clear();
          for (var shop in snapshot.data!) {
            _markers.add(Marker(
              markerId: MarkerId(shop.shopName),
              position: LatLng(shop.location.latitude, shop.location.longitude),
              infoWindow: InfoWindow(
                title: shop.shopName,
                snippet: shop.address,
              ),
            ));
          }

          return GoogleMap(
            onMapCreated: _onMapCreated,
            initialCameraPosition: CameraPosition(
              target: _markers.isNotEmpty
                  ? _markers.first.position
                  : const LatLng(
                      35.6812, 139.7671), // Default to Tokyo if no shops
              zoom: 11.0,
            ),
            markers: _markers,
          );
        },
      ),
    );
  }
}

class Shop {
  final String shopName;
  final String address;
  final GeoPoint location;

  Shop({required this.shopName, required this.address, required this.location});

  factory Shop.fromFirestore(DocumentSnapshot doc) {
    Map data = doc.data() as Map<String, dynamic>;
    return Shop(
      shopName: data['shop_name'] ?? '',
      address: data['address'] ?? '',
      location: data['location'] ?? GeoPoint(0, 0),
    );
  }
}

ビルドするとこのように保存した位置情報の場所に赤いピンが立ちます!

感想

いかがでしたでしょうか。GoogleMapに、Cloud Firestoreの位置情報を表示する記事が探してもないので試しに作ってみました。
地図アプリは個人的に好きなので、個人開発で使ってみたいと思いつつ課金が怖いのでやっておりません💦
SwiftのMapkitであれば大丈夫のようですが.....

Discussion