0
votes

I have a problem querying my geolocation data from my database in Cloud Firestore. I went through the documentation on Youtube and came to the conclusion that it will work best for me when i save the geolocation data in subcollections.

Here is my databse structure:

enter image description here

And if you go into one of the documents in the subcollection:

enter image description here

The database itself has a collection called "tourguides", with each document containing basic info like the name of the tour and the region where the tour is (both are Strings). Each of the documents then have a subcollection called "locations", where each document has the Strings "Name" and "ID" and also a geopoint with latitude and longitude data. The documents from the "Tourguides" collection are shown in a ListView. Whenever i tap on one of the entries, a map shall open where all the markers from the respective subcollection are shown.

Here is my ListView Builder:

@override
void initState() {
super.initState();
_pointsofinterest =    Firestore.instance.collection('tourguides').document('sydney_downtown_guide').col  lection('locations').orderBy('name').snapshots();
super.initState();
  }

  @override
 Widget build(BuildContext context) {
return Scaffold(
  body: StreamBuilder<QuerySnapshot>(
    stream: _pointsofinterest,
    builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
      if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
      switch (snapshot.connectionState) {
        case ConnectionState.waiting:
          return new Text('Loading...');
        default:
          return new ListView(
            children:
                snapshot.data.documents.map((DocumentSnapshot document) {
              return InkWell(
                child: new ListTile(
                  title: new Text(document['name']),
                  subtitle: new Text(document['region']),
                  onTap: () {
                    return TourMap(
                      documents: snapshot.data.documents,
                      initialPosition: const LatLng(-33.868210, 151.208391),
                      mapController: _mapController,
                    );
                  },
                ),
              );
            }).toList(),
          );
      }
    },
  ),
);
 }

I put my map into a StatlessWidget (I am not sure. Maybe this has to be a StatefulWidget?):

class TourMap extends StatelessWidget {
const TourMap({
Key key,
@required this.documents,
@required this.initialPosition,
@required this.mapController,
}) : super(key: key);

final List<DocumentSnapshot> documents;
final LatLng initialPosition;
final Completer<GoogleMapController> mapController;

@override
Widget build(BuildContext context) {
 return GoogleMap(
  initialCameraPosition: CameraPosition(
    target: initialPosition,
    zoom: 12,
  ),
  markers: documents
      .map((document) => Marker(
            markerId: MarkerId(document['placeId']),
            icon: BitmapDescriptor.defaultMarker,
            position: LatLng(
              document['geolocation'].latitude,
              document['geolocation'].longitude,
            ),
            infoWindow: InfoWindow(
              title: document['location_name'],
            ),
          ))
      .toSet(),
  onMapCreated: (mapController) {
    this.mapController.complete(mapController);
  },
);
   }}

Now i dont exactly know how to set up the query in my OnTap function. The Firestore documentation showed that i always have to refer to specfic documents if i go down the collections from my database.

For example (collection/document/collecion). But in my query, the "document" in the middle of the path is always different, depending on which tourguide the user clicks.

Any ideas on that? Looking Forward to your replies!

UPDATE: I slightly configured my database structure! I now use two seperate databases. One database holds information about the available tourguides (just two Strings currently: Name and Region) and the other stores the actual individual Locations. I now use where-queries to get the correct locations based on the tourguide´s Name they´re belonging to.

The query itself works on the OnTap function now:

 return new ListView(
            children:
                snapshot.data.documents.map((DocumentSnapshot document) {
              return InkWell(
                child: new ListTile(
                  title: new Text(document['name']),
                  subtitle: new Text(document['region']),
                  onTap: () {
                    Firestore.instance.collection('locations').where(
                          "toActivity",
                          isEqualTo: document['name'],
                        )
                        .snapshots()
                        .listen((data) =>
                        data.documents.forEach((doc) => print(doc["location_name"])));
                  },
                ),
              );
            }).toList(),
          );

The database structure:

enter image description here

enter image description here

The correct entries are printed into the console if i tap on one of the entries in the ListView. But i will need a Google Map to pop up that shows the appropriate markers based on the "geolocation" values from the database.

2
The question is a bit broad and without seeing your structure, it's going to be hard to answer. You should also include the code you've attempted so we can see what the thought process is. Please update your question. - Jay
@Jay Thank you for the reply. I am not sure if i have to set up a new Firebase connection in my map Widget. I cant reach my subcollections at the moment. - Dee Jott
@Jay I updated my code and my question. My approach is a bit different now. Maybe this faciliates something. - Dee Jott

2 Answers

1
votes

Bro, I did. I can retrieve it by two ways. 1. manually by using simple 'initState'. 2. second by using provider (but by using the second method, i have not succeed to show the marker, yet). Hope can help you, although it was long time ago. Here is mine by using 'initState':

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:provider/provider.dart';
import 'package:visitmalang/provider/aktivitas_provider.dart';
import 'package:visitmalang/ui/home/beranda/aktivitas/profil_agen_wisata.dart';

import 'package:visitmalang/ui/widget/textcustom.dart';

class MapAktivitas extends StatefulWidget {
  @override
  _MapAktivitasState createState() => _MapAktivitasState();
}

class _MapAktivitasState extends State<MapAktivitas> {
  Map<MarkerId, Marker> markers = <MarkerId, Marker>{};

  GoogleMapController mapController;

  bool mapToggle = false;
  bool geraiToggle = false;

  var currentLocation;
  var clients = [];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    Geolocator().getCurrentPosition().then((lokasiSekarang) {
      setState(() {
        currentLocation = lokasiSekarang;
        mapToggle = true;
        populateClients();
      });
    });
  }

  populateClients() {
    clients = [];
    Firestore.instance.collection('trail').getDocuments().then((docs) {
      if (docs.documents.isNotEmpty) {
        setState(() {
          geraiToggle = true;
        });

        for (int i = 0; i < docs.documents.length; i++) {
          clients.add(docs.documents[i].data);
          initMarker(docs.documents[i].data, docs.documents[i].documentID);
        }
      }
    });
  }

  void initMarker(request, requestId) {
    var markerIdVal = requestId;
    final MarkerId markerId = MarkerId(markerIdVal);
    final Marker marker = Marker(
        markerId: markerId,
        position: LatLng(
            request['koordinat'].latitude, request['koordinat'].longitude),
        infoWindow: InfoWindow(title: request['nama']));
    setState(() {
      markers[markerId] = marker;
    });
  }

  Widget clientCard(client) {
    return Padding(
      padding: const EdgeInsets.only(left: 8.0, top: 8.0),
      child: InkWell(
          onTap: () {
            zoomInMarker(client);
          },
          child: Stack(
            alignment: FractionalOffset(0.5, 0.94),
            children: <Widget>[
              Material(
                elevation: 4.0,
                borderRadius: BorderRadius.circular(10.0),
                child: Container(
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(10.0),
                    color: Colors.white,
                  ),
                  height: 108,
                  width: 200,
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
//
                      ClipRRect(
                        borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(10.0),
                            bottomLeft: Radius.circular(10.0)),
                        child: Container(
                          width: 96,
                          height: 108,
                          child: Image.asset(
                            'assets/trail.png',
                            fit: BoxFit.cover,
                          ),
                        ),
                      ),
                      SizedBox(
                        width: 4,
                      ),
                      Stack(
                        alignment: FractionalOffset(0.5, 0.9),
                        children: <Widget>[
                          Container(
                            width: 100,
                            child: Padding(
                              padding: const EdgeInsets.only(top: 8.0),
                              child: Column(
                                children: <Widget>[
                                  Padding(
                                    padding: const EdgeInsets.symmetric(
                                        horizontal: 8.0),
                                    child: Container(
                                      alignment: Alignment.center,
                                      child: textCustom(client['nama'],
                                          Colors.black87, 14, 'Montserrat'),
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          ),
                          ClipRRect(
                            borderRadius: BorderRadius.circular(10),
                            child: InkWell(
                              onTap: () => Navigator.of(context).push(
                                  CupertinoPageRoute(
                                      builder: (BuildContext context) =>
                                          ProfilAgenWisata(
                                            nama: client['nama'],
                                            deskripsi: client['deskripsi'],
                                            website: client['website'],
                                            email: client['email'],
                                            noTelepon: client['noTelepon'],
                                            whatsApp: client['whatsApp'],
                                            alamat: client['alamat'],
                                            fasilitas: client['fasilitas'],
                                          ))),
                              child: Container(
                                alignment: Alignment.center,
                                color: Color(0xFFDB5C48),
                                height: 40,
                                width: 88,
                                child: textCustom(
                                    'Detail', Colors.white, 14, 'Montserrat'),
                              ),
                            ),
                          )
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            ],
          )),
    );
  }

  zoomInMarker(client) {
    mapController.animateCamera(
      CameraUpdate.newCameraPosition(
        CameraPosition(
            target: LatLng(
                client['koordinat'].latitude, client['koordinat'].longitude),
            zoom: 16.0,
            bearing: 19.0,
            tilt: 15.0),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
//    AktivitasNotifier aktivitasNotifier = Provider.of<AktivitasNotifier>(context);
    return Scaffold(
        appBar: AppBar(
          title: textCustom('Ngetrail', Colors.black87, 18, 'Montserrat'),
          centerTitle: true,
          elevation: 0.0,
          backgroundColor: Colors.transparent.withOpacity(0.0),
          leading: IconButton(
              icon: Icon(Icons.arrow_back_ios, color: Colors.black87),
          onPressed: (){Navigator.pop(context);},),
        ),
        body: Stack(
          children: <Widget>[
            Column(
              children: <Widget>[
                Container(
                  width: double.infinity,
                  height: MediaQuery.of(context).size.height - 80,
                  child: mapToggle
                      ? GoogleMap(
                          myLocationEnabled: true,
                          myLocationButtonEnabled: true,
                          markers: Set<Marker>.of(markers.values),
                          compassEnabled: false,
                          zoomControlsEnabled: false,
                          mapType: MapType.normal,
                          initialCameraPosition: CameraPosition(
                              target: LatLng(currentLocation.latitude,
                                  currentLocation.longitude),
                              zoom: 15),
                          onMapCreated: (controller) {
                            setState(() {
                              mapController = controller;
                            });
                          },
                        )
                      : Center(
                          child: textCustom(
                              'Loading...', Colors.black87, 20, 'Hind')),
                ),
              ],
            ),
            Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                Container(
                  height: 140.0,
                  width: MediaQuery.of(context).size.width,
                  child: geraiToggle
                      ? ListView(
                          scrollDirection: Axis.horizontal,
                          padding: EdgeInsets.all(8.0),
                          children: clients.map((element) {
                            return clientCard(element);
                          }).toList(),
                        )
                      : Container(),
                ),
                SizedBox(
                  height: 56.0,
                )
              ],
            )
          ],
        ));
  }
}
0
votes

Here is the second method, by using provider.

First, you have to decide the model (just focus on 'koordinat'):

    class ModelAktivitas {
  String idAktivitas;
  var koordinat;
  String nama;
  String deskripsi;
  String alamat;
  String email;
  String noTelepon;
  String website;
  String gambarUtama;
  List galeri;
  List fasilitas;

  ModelAktivitas.fromMap(Map<String, dynamic> data) {
    idAktivitas = data['idAktivitas'];
    koordinat = data['koordinat'];
    nama = data['nama'];
    deskripsi = data['deskripsi'];
    noTelepon = data['nomorTelepon'];
    galeri = data['galeri'];
    fasilitas = data['fasilitas'];
    alamat = data['alamat'];
    email = data['alamat'];
    gambarUtama = data['gambarUtama'];
    website = data['website'];
  }
}

second, make provider for it:

import 'dart:collection';
import 'package:flutter/cupertino.dart';
import 'package:visitmalang/models/aktivitas_model.dart';

class AktivitasNotifier with ChangeNotifier {
  List<ModelAktivitas> _listAktivitas = [];
  ModelAktivitas _detailAktivitas;

  UnmodifiableListView<ModelAktivitas> get listAktivitas =>
      UnmodifiableListView(_listAktivitas);

  ModelAktivitas get detailAktivitas => _detailAktivitas;

  set listAktivitas(List<ModelAktivitas> listAktivitas) {
    _listAktivitas = listAktivitas;
    notifyListeners();
  }

  set detailAktivitas(ModelAktivitas aktivitas) {
    _detailAktivitas = aktivitas;
    notifyListeners();
  }
}

After that, add the "get" from your firestore services:

getListAktivitas(AktivitasNotifier aktivitasNotifier) async {
  QuerySnapshot snapshot =
  await Firestore.instance.collection('trail').getDocuments();

  List<ModelAktivitas> _listAktivitas = [];
  snapshot.documents.forEach((doc) {
    ModelAktivitas modelAktivitas = ModelAktivitas.fromMap(doc.data);
    _listAktivitas.add(modelAktivitas);
  });
  aktivitasNotifier.listAktivitas = _listAktivitas;

}

then, the last step stream it to your UI code:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:provider/provider.dart';
import 'package:visitmalang/provider/aktivitas_provider.dart';
import 'package:visitmalang/ui/home/beranda/aktivitas/profil_agen_wisata.dart';
import 'package:visitmalang/ui/widget/textcustom.dart';
import 'package:visitmalang/service/user_service.dart';

class MapAktivitasTandingan extends StatefulWidget {
  @override
  _MapAktivitasTandinganState createState() => _MapAktivitasTandinganState();
}

class _MapAktivitasTandinganState extends State<MapAktivitasTandingan> {
  Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
  GoogleMapController mapController;
  bool mapToggle = false;
  bool geraiToggle = false;
  var currentLocation;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    Geolocator().getCurrentPosition().then((lokasiSekarang) {
      setState(() {
        currentLocation = lokasiSekarang;
        mapToggle = true;
      });
    });

    AktivitasNotifier aktivitasNotifier =
        Provider.of<AktivitasNotifier>(context, listen: false);
    getListAktivitas(aktivitasNotifier);
  }

  @override
  Widget build(BuildContext context) {
    AktivitasNotifier aktivitasNotifier =
        Provider.of<AktivitasNotifier>(context);
    return Scaffold(
        appBar: AppBar(
          title: textCustom('Ngetrail', Colors.black87, 18, 'Montserrat'),
          centerTitle: true,
          elevation: 0.0,
          backgroundColor: Colors.transparent.withOpacity(0.0),
          leading: IconButton(
            icon: Icon(Icons.arrow_back_ios, color: Colors.black87),
            onPressed: () {
              Navigator.pop(context);
            },
          ),
        ),
        body: Stack(
          children: <Widget>[
            Column(
              children: <Widget>[
                Container(
                  width: double.infinity,
                  height: MediaQuery.of(context).size.height - 80,
                  child: mapToggle
                      ? GoogleMap(
                          myLocationEnabled: true,
                          myLocationButtonEnabled: true,
                          markers: {

                          },
                          compassEnabled: false,
                          zoomControlsEnabled: false,
                          mapType: MapType.normal,
                          initialCameraPosition: CameraPosition(
                              target: LatLng(currentLocation.latitude,
                                  currentLocation.longitude),
                              zoom: 15),
                          onMapCreated: (controller) {
                            setState(() {
                              mapController = controller;
                            });
                          },
                        )
                      : Center(
                          child: textCustom(
                              'Loading...', Colors.black87, 20, 'Hind')),
                ),
              ],
            ),
            Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                Container(
                    height: 140.0,
                    width: MediaQuery.of(context).size.width,
                    child: ListView.builder(
                      scrollDirection: Axis.horizontal,
                      itemCount: aktivitasNotifier.listAktivitas.length,
                      itemBuilder: (BuildContext context, int index) {
                        return Padding(
                          padding: const EdgeInsets.only(left: 8.0, top: 8.0),
                          child: InkWell(
                              onTap: () {
                                mapController.animateCamera(
                                  CameraUpdate.newCameraPosition(
                                    CameraPosition(
                                        target: LatLng(
                                            aktivitasNotifier
                                                .listAktivitas[index]
                                                .koordinat
                                                .latitude,
                                            aktivitasNotifier
                                                .listAktivitas[index]
                                                .koordinat
                                                .longitude),
                                        zoom: 16.0,
                                        bearing: 19.0,
                                        tilt: 15.0),
                                  ),
                                );
                              },
                              child: Stack(
                                alignment: FractionalOffset(0.5, 0.94),
                                children: <Widget>[
                                  Material(
                                    elevation: 4.0,
                                    borderRadius: BorderRadius.circular(10.0),
                                    child: Container(
                                      decoration: BoxDecoration(
                                        borderRadius:
                                            BorderRadius.circular(10.0),
                                        color: Colors.white,
                                      ),
                                      height: 108,
                                      width: 200,
                                      child: Row(
                                        crossAxisAlignment:
                                            CrossAxisAlignment.center,
                                        children: <Widget>[
//
                                          ClipRRect(
                                            borderRadius: BorderRadius.only(
                                                topLeft: Radius.circular(10.0),
                                                bottomLeft:
                                                    Radius.circular(10.0)),
                                            child: Container(
                                              width: 96,
                                              height: 108,
                                              child: Image.asset(
                                                'assets/trail.png',
                                                fit: BoxFit.cover,
                                              ),
                                            ),
                                          ),
                                          SizedBox(
                                            width: 4,
                                          ),
                                          Stack(
                                            alignment:
                                                FractionalOffset(0.5, 0.9),
                                            children: <Widget>[
                                              Container(
                                                width: 100,
                                                child: Padding(
                                                  padding:
                                                      const EdgeInsets.only(
                                                          top: 8.0),
                                                  child: Column(
                                                    children: <Widget>[
                                                      Padding(
                                                        padding:
                                                            const EdgeInsets
                                                                    .symmetric(
                                                                horizontal:
                                                                    8.0),
                                                        child: Container(
                                                          alignment:
                                                              Alignment.center,
                                                          child: textCustom(
                                                              aktivitasNotifier
                                                                  .listAktivitas[
                                                                      index]
                                                                  .nama,
                                                              Colors.black87,
                                                              14,
                                                              'Montserrat'),
                                                        ),
                                                      ),
                                                    ],
                                                  ),
                                                ),
                                              ),
                                              ClipRRect(
                                                borderRadius:
                                                    BorderRadius.circular(10),
                                                child: InkWell(
                                                  onTap: () => Navigator.of(
                                                          context)
                                                      .push(CupertinoPageRoute(
                                                          builder: (BuildContext
                                                                  context) =>
                                                              ProfilAgenWisata(
                                                                nama: aktivitasNotifier
                                                                    .listAktivitas[
                                                                        index]
                                                                    .nama,
                                                                website: aktivitasNotifier
                                                                    .listAktivitas[
                                                                        index]
                                                                    .website,
                                                                noTelepon: aktivitasNotifier
                                                                    .listAktivitas[
                                                                        index]
                                                                    .noTelepon,
                                                                email: aktivitasNotifier
                                                                    .listAktivitas[
                                                                        index]
                                                                    .email,
                                                                alamat: aktivitasNotifier
                                                                    .listAktivitas[
                                                                        index]
                                                                    .alamat,
                                                                deskripsi: aktivitasNotifier
                                                                    .listAktivitas[
                                                                        index]
                                                                    .deskripsi,

                                                              ))),
                                                  child: Container(
                                                    alignment: Alignment.center,
                                                    color: Color(0xFFDB5C48),
                                                    height: 40,
                                                    width: 88,
                                                    child: textCustom(
                                                        'Detail',
                                                        Colors.white,
                                                        14,
                                                        'Montserrat'),
                                                  ),
                                                ),
                                              )
                                            ],
                                          ),
                                        ],
                                      ),
                                    ),
                                  ),
                                ],
                              )),
                        );
                      },
                    )),
                SizedBox(
                  height: 56.0,
                )
              ],
            )
          ],
        ));
  }
}