Performing operations on maps when clicking on a result in flutter

21 views Asked by At

I have a mobile application, I show markers on Google Maps, then I open those markers and direct them to the search page with a search bar at the top. When the user searches for something and clicks on it, I want him to zoom in on the location of the neighborhood he clicked on and open the dialog, but I couldn't do it, I know it's a bit complicated but I need help.

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

  @override
  State<GoogleMapsScreen> createState() => _GoogleMapsScreenState();
}

class _GoogleMapsScreenState extends State<GoogleMapsScreen> {
  GoogleMapController? mapController;
  Future<void> _goToCurrentLocation() async {
    var currentLocation = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high);
    mapController?.animateCamera(CameraUpdate.newCameraPosition(CameraPosition(
      target: LatLng(currentLocation.latitude, currentLocation.longitude),
      zoom: 17.0,
    )));
  }

  final TextEditingController searchController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => MahallelerBloc()..add(LoadMahalleler()),
      child: Scaffold(
        body: BlocBuilder<MahallelerBloc, MahallelerState>(
          builder: (context, state) {
            if (state is MahallelerLoaded) {
              return Stack(
                children: [
                  GoogleMap(
                    myLocationButtonEnabled: false,
                    onMapCreated: (GoogleMapController controller) {
                      print('Harita oluşturuldu');
                      mapController = controller;
                      BlocProvider.of<MahallelerBloc>(context).mapController =
                          controller;
                    },
                    zoomControlsEnabled: false,
                    myLocationEnabled: true,
                    mapToolbarEnabled: false,
                    mapType: MapType.normal,
                    initialCameraPosition: const CameraPosition(
                      target: LatLng(37.3122872, 40.7339973),
                      zoom: 14,
                    ),
                    markers: Set<Marker>.of(
                      state.markers.map((marker) {
                        return Marker(
                          markerId: marker.markerId,
                          position: marker.position,
                          icon: marker.icon,
                          onTap: () async {
                            final mahallelerState =
                                BlocProvider.of<MahallelerBloc>(context).state;
                            String seciliSecimID = "Varsayılan ID";

                            if (mahallelerState is MahallelerLoaded) {
                              seciliSecimID =
                                  mahallelerState.selectedElection ??
                                      "Varsayılan ID";
                            }
                            final markerIdValue = marker.markerId.value;
                            final bilgi = await FirestoreService()
                                .getMahalleVeIlceBilgisi(markerIdValue);
                            final secimSonuclari = await FirestoreService()
                                .getElectionResults(
                                    markerIdValue, seciliSecimID);
                            final secimBilgileri = await FirestoreService()
                                .getElectionInfo(markerIdValue, seciliSecimID);
                            if (!mounted) {
                              return;
                            }
                            showDialog(
                              context: context,
                              builder: (context) {
                                List<String> siraliAdayIsimleri = [];
                                List<int> siraliOyOranlari = [];

                                for (var adayId in secimAdaySiralama) {
                                  if (secimSonuclari.containsKey(adayId)) {
                                    siraliAdayIsimleri.add(
                                        secimAdayIsimleri[adayId] ??
                                            "Bilinmeyen Aday");
                                    siraliOyOranlari
                                        .add(secimSonuclari[adayId]);
                                  }
                                }
                                List<List<Color>> secilenGradientRenkler = [];
                                for (var adayIsim in siraliAdayIsimleri) {
                                  var gradientRenk = adayRenkleri[adayIsim] ??
                                      [Colors.grey, Colors.white];
                                  secilenGradientRenkler.add(gradientRenk);
                                }

                                Map<String, String> duzenliSecimBilgileri = {};
                                for (var key in secimBilgileriSiralama) {
                                  if (secimBilgileri.containsKey(key)) {
                                    String yeniKey =
                                        secimBilgilerikeys[key] ?? key;
                                    String deger =
                                        secimBilgileri[key].toString();
                                    duzenliSecimBilgileri[yeniKey] = deger;
                                  }
                                }

                                return PieChartDialog(
                                    markerId: markerIdValue,
                                    mahalleAdi: bilgi['mahalleAdi'],
                                    ilceAdi: bilgi['ilceAdi'],
                                    seciliSecim: secimAdlari[seciliSecimID] ??
                                        "Bilinmeyen Seçim",
                                    secimAdaylari: siraliAdayIsimleri,
                                    secimSonuclari: siraliOyOranlari,
                                    gradientColors: secilenGradientRenkler,
                                    secimBilgileri: duzenliSecimBilgileri);
                              },
                            );
                          },
                        );
                      }),
                    ),
                  ),
                  Positioned(
                    right: 30,
                    bottom: 170,
                    child: FloatingActionButton(
                      heroTag: "konumum",
                      backgroundColor: Colors.white,
                      foregroundColor: const Color.fromRGBO(29, 66, 115, 1),
                      onPressed: _goToCurrentLocation,
                      child: const Icon(IconlyBold.location),
                    ),
                  ),
                  Positioned(
                    top: MediaQuery.of(context).padding.top + 20,
                    left: 20,
                    right: 20,
                    child: SearchTextField(
                      keyboardType: TextInputType.none,
                      showCursor: false,
                      controller: searchController,
                      prefixIconData: IconlyLight.search,
                      suffixIconData: IconlyLight.filter,
                      onTap: () {
                        Navigator.of(context).push(
                          MaterialPageRoute(
                            builder: (context) => SearchScreen(
                                selectedElectionId:
                                    state.selectedElection ?? 'BSB2019',
                                controller: searchController),
                          ),
                        );
                      },
                    ),
                  ),
                  Positioned(
                    right: 30,
                    bottom: 110,
                    child: FloatingActionButton(
                      heroTag: "Seçimler",
                      backgroundColor: Colors.white,
                      foregroundColor: const Color.fromRGBO(29, 66, 115, 1),
                      child: const Icon(IconlyBroken.chart),
                      onPressed: () {
                        showElectionDialog(context);
                      },
                    ),
                  ),
                ],
              );
            } else if (state is MahallelerError) {
              return const Center(child: Text('Bir hata oluştu.'));
            }
            return const Center(child: CircularProgressIndicator());
          },
        ),
      ),
    );
  }
}

Search Screen

class SearchScreen extends StatefulWidget {
  final TextEditingController controller;
  final String selectedElectionId;

  const SearchScreen({
    super.key,
    required this.controller,
    required this.selectedElectionId,
  });

  @override
  State<SearchScreen> createState() => _SearchScreenState();
}

class _SearchScreenState extends State<SearchScreen> {
  final FocusNode _focusNode = FocusNode();
  List<SearchResultCard> searchResults = [];
  String currentSortingOption = "A'dan Z'ye";

  @override
  void initState() {
    super.initState();
    widget.controller.addListener(_onSearchChanged);
    _fetchMahalleler(widget.selectedElectionId, currentSortingOption, '');
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (_focusNode.canRequestFocus) {
        _focusNode.requestFocus();
      }
    });
  }

  void _onSearchChanged() {
    String searchQueryLowercase = widget.controller.text.toLowerCase();
    _fetchMahalleler(
        widget.selectedElectionId, currentSortingOption, searchQueryLowercase);
  }
Future<void> _fetchMahalleler(
    String selectedElection, String sortingOption, String searchQuery) async {
  List<SearchResultCard> fetchedResults = [];
  try {
    Query query = FirebaseFirestore.instance.collection('mahalleler').where(
        'secimler.$selectedElection.secimSonuclari',
        isNotEqualTo: null);

    if (searchQuery.isNotEmpty) {
      query = query
          .where('genelBilgiler.mahalleKoyKucukHarf',
              isGreaterThanOrEqualTo: searchQuery)
          .where('genelBilgiler.mahalleKoyKucukHarf',
              isLessThanOrEqualTo: '$searchQuery\uf8ff');
    }

    var querySnapshot = await query.get();

    List<DocumentSnapshot> docs = querySnapshot.docs;
    List<Map<String, dynamic>> tempDocs =
        docs.map((doc) => doc.data() as Map<String, dynamic>).toList();

    if (sortingOption == "Z'den A'ya") {
      tempDocs.sort((a, b) => _turkishStringCompare(
          b['genelBilgiler']['mahalleKoyKucukHarf'],
          a['genelBilgiler']['mahalleKoyKucukHarf']));
    } else {
      tempDocs.sort((a, b) => _turkishStringCompare(
          a['genelBilgiler']['mahalleKoyKucukHarf'],
          b['genelBilgiler']['mahalleKoyKucukHarf']));
    }

    for (var doc in docs) {
      var data = doc.data() as Map<String, dynamic>;
      var secimSonuclari = data['secimler'][selectedElection]['secimSonuclari']
              as Map<String, dynamic>? ??
          {};

      List<Candidate> adaylar = secimAdaySiralama
          .where((key) => secimSonuclari.containsKey(key))
          .map((key) {
        var adayAdi = secimAdayIsimleri[key]!;
        var oySayisi = secimSonuclari[key];
        var renkler = adayRenkleri[adayAdi] ?? [Colors.grey, Colors.grey];
        return Candidate(
            adayAdi: adayAdi, aldigiOy: oySayisi, gradient: LinearGradient(colors: renkler));
      }).toList();

      var enYuksekOyAlanPartiKey = secimSonuclari.entries
          .reduce((a, b) => a.value > b.value ? a : b)
          .key;
      var markerIconPath = MarkerIconPaths.getIconPath(
          selectedElection, enYuksekOyAlanPartiKey);

      var konum = data['genelBilgiler']['konum'] as GeoPoint;

      fetchedResults.add(SearchResultCard(
          adaylar: adaylar,
          mahalleId: doc.id, // Belge kimliği burada kullanılıyor
          latitude: konum.latitude,
          longitude: konum.longitude,
          ilce: data['genelBilgiler']['ilceAdi'],
          mahalle: data['genelBilgiler']['mahalleKoy'],
          kazananinResmi: markerIconPath));
     print('Adaylar: $adaylar, MahalleID: ${doc.id}, Latitude: ${konum.latitude}, Longitude: ${konum.longitude}, İlçe: ${data['genelBilgiler']['ilceAdi']}, Mahalle: ${data['genelBilgiler']['mahalleKoy']}, Kazananın Resmi: $markerIconPath');
    }

    setState(() {
      searchResults = fetchedResults;
    });
  } catch (e) {
    if (kDebugMode) {
      print('Error fetching mahalleler: $e');
    }
  }
}

  int _turkishStringCompare(String a, String b) {
    const order = 'aAbBcCçÇdDeEfFgGğĞhHıIiİjJkKlLmMnNoOöÖpPrRsSşŞtTuUüÜvVyYzZ';
    for (int i = 0; i < a.length && i < b.length; i++) {
      final ai = order.indexOf(a[i]);
      final bi = order.indexOf(b[i]);
      if (ai != bi) return ai.compareTo(bi);
    }
    return a.length.compareTo(b.length);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Padding(
            padding: EdgeInsets.only(
              top: MediaQuery.of(context).padding.top + 20,
              left: 20,
              right: 20,
            ),
            child: SearchTextField(
              controller: widget.controller,
              focusNode: _focusNode,
              clearTextOnSuffixIconTap: true,
              returnOnSuffixIconTap: true,
              prefixIconData: IconlyLight.search,
              suffixIconData: CupertinoIcons.clear,
            ),
          ),
          SearchFilterWidget(
            onSelectedSorting: (selectedOption) {
              setState(() {
                currentSortingOption = selectedOption;
                _fetchMahalleler(widget.selectedElectionId,
                    currentSortingOption, widget.controller.text);
              });
            },
          ),
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.only(top: 20),
              itemCount: searchResults.length,
              itemBuilder: (context, index) {
                return searchResults[index];
              },
            ),
          ),
        ],
      ),
    );
  }
}

Search Result Card

class SearchResultCard extends StatefulWidget {
  final String kazananinResmi;
  final String mahalle;
  final String ilce;
  final List<Candidate> adaylar;
  // Yeni eklenen alanlar:
  final String mahalleId;
  final double latitude;
  final double longitude;

  const SearchResultCard({
    super.key,
    required this.kazananinResmi,
    required this.mahalle,
    required this.ilce,
    required this.adaylar,
    // Yeni eklenen alanların constructor içinde tanımlanması:
    required this.mahalleId,
    required this.latitude,
    required this.longitude,
  });

  @override
  SearchResultCardState createState() => SearchResultCardState();
}

class SearchResultCardState extends State<SearchResultCard> {
  final ScrollController _scrollController = ScrollController();
  Timer? _timer;
  final double scrollAmount = 3.0;
  final int scrollDelay = 100;
  final Duration waitDuration = const Duration(seconds: 3);

  void _startAutoScroll() {
    _timer = Timer.periodic(Duration(milliseconds: scrollDelay), (timer) {
      double maxScroll = _scrollController.position.maxScrollExtent;
      double currentScroll = _scrollController.offset;

      if (currentScroll < maxScroll) {
        _scrollController.animateTo(
          currentScroll + scrollAmount,
          duration: Duration(milliseconds: scrollDelay),
          curve: Curves.linear,
        );
      } else {
        _timer?.cancel();
        _scrollController.jumpTo(0.0);

        Future.delayed(waitDuration, () {
          if (mounted) {
            _startAutoScroll();
          }
        });
      }
    });
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => _startAutoScroll());
  }

  @override
  void dispose() {
    _timer?.cancel();
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        print("Karta tıklandı: ${widget.mahalleId}");
        // Burada, BLoC'a MahalleSecildi event'ini gönderin
        BlocProvider.of<MahallelerBloc>(context).add(
          MahalleSecildi(
            mahalleId: widget.mahalleId,
            latitude: widget.latitude,
            longitude: widget.longitude,
          ),
        );
        Navigator.of(context).pop();
      },
      child: Card(
        color: Colors.white,
        surfaceTintColor: Colors.white,
        shadowColor: const Color.fromRGBO(194, 217, 26, 1),
        margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Image.asset(
                widget.kazananinResmi,
                width: 45,
                height: 45,
                fit: BoxFit.cover,
              ),
              const SizedBox(width: 8.0),
              Expanded(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      widget.mahalle,
                      style: const TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    SizedBox(
                      height: 35,
                      child: ListView.builder(
                        scrollDirection: Axis.horizontal,
                        controller: _scrollController,
                        itemCount: widget.adaylar.length,
                        itemBuilder: (context, index) {
                          final candidate = widget.adaylar[index];
                          return Padding(
                            padding: const EdgeInsets.only(right: 8.0),
                            child: Row(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Container(
                                  width: 13,
                                  height: 13,
                                  decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    gradient: candidate.gradient,
                                  ),
                                ),
                                const SizedBox(width: 5.0),
                                Text(
                                    '${candidate.adayAdi}: ${candidate.aldigiOy}',
                                    style: const TextStyle(fontSize: 14)),
                              ],
                            ),
                          );
                        },
                      ),
                    ),
                    Align(
                      alignment: Alignment.topRight,
                      child: Text(
                        widget.ilce,
                        style: const TextStyle(
                          fontSize: 12,
                          fontWeight: FontWeight.bold,
                          color: Colors.black54,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class Candidate {
  final String adayAdi;
  final int aldigiOy;
  final LinearGradient gradient;

  Candidate({
    required this.adayAdi,
    required this.aldigiOy,
    required this.gradient,
  });
}

Bloc


class MahallelerBloc extends Bloc<MahallelerEvent, MahallelerState> {
  String? selectedElection;
  GoogleMapController? mapController; // Google Maps Controller'ı tanımla
  MahallelerBloc() : super(MahallelerInitial()) {
    on<LoadMahalleler>(_onLoadMahalleler);
    on<SelectElection>(_onSelectElection);
    on<MahalleSecildi>(_onMahalleSecildi); // Yeni event handler'ı ekle
    _loadSelectedElection();
  }


  Future<void> _loadSelectedElection() async {
    final prefs = await SharedPreferences.getInstance();

    selectedElection = prefs.getString('selectedElection') ?? 'BSB2019';
    add(LoadMahalleler());
  }

  Future<void> _onLoadMahalleler(
      LoadMahalleler event, Emitter<MahallelerState> emit) async {
    try {
      final election = selectedElection ?? 'BSB2019';

      final Set<Marker> markers = {};
      var querySnapshot =
          await FirebaseFirestore.instance.collection('mahalleler').get();
      Map<String, String> elections = await _fetchElections();

      for (var mahalleDoc in querySnapshot.docs) {
        var secimSonuclari = mahalleDoc.data()['secimler'][election]
            ['secimSonuclari'] as Map<String, dynamic>?;
        if (secimSonuclari != null) {
          var enYuksekOyAlanParti = secimSonuclari.entries
              .reduce((a, b) => a.value > b.value ? a : b)
              .key;
          String markerIconPath =
              MarkerIconPaths.getIconPath(election, enYuksekOyAlanParti);

          GeoPoint konum = mahalleDoc.data()['genelBilgiler']['konum'];
          var markerId = MarkerId(mahalleDoc.id);

          BitmapDescriptor icon = await getCustomIcon(markerIconPath);

          var marker = Marker(
            markerId: markerId,
            icon: icon,
            position: LatLng(konum.latitude, konum.longitude),
          );

          markers.add(marker);
        }
      }

      emit(MahallelerLoaded(markers, elections, selectedElection: election));
    } catch (e) {
      emit(MahallelerError());
      if (kDebugMode) {
        print('Hata: $e');
      }
    }
  }
 Future<void> _onMahalleSecildi(MahalleSecildi event, Emitter<MahallelerState> emit) async {
  try {
    print('Zoom işlemi başlıyor...'); // İşlemin başladığını belirten log.
    await mapController?.animateCamera(
      CameraUpdate.newCameraPosition(
        CameraPosition(
          target: LatLng(event.latitude, event.longitude),
          zoom: 18.0,
        ),
      ),
    );
    print('Zoom işlemi tamamlandı.'); // İşlemin tamamlandığını belirten log.
  } catch (e) {
    print('Zoom işlemi sırasında bir hata oluştu: $e'); // Hata oluştuğunu belirten log.
  }

  // İsteğe bağlı: Seçilen mahalleyi işaretleyen bir marker ekleyin veya mevcut marker'ları güncelleyin
  // Bu kısımda, marker listesini güncelleyebilir ve emit ile durumu güncelleyebilirsiniz.
}



  Future<void> _onSelectElection(
      SelectElection event, Emitter<MahallelerState> emit) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('selectedElection', event.electionId);
      selectedElection = event.electionId;

      final Set<Marker> markers = {};
      var querySnapshot =
          await FirebaseFirestore.instance.collection('mahalleler').get();
      Map<String, String> elections = await _fetchElections();

      for (var doc in querySnapshot.docs) {
        var data = doc.data();
        var secimSonuclari = data['secimler']?[event.electionId]
            ?['secimSonuclari'] as Map<String, dynamic>?;
        if (secimSonuclari != null) {
          var enYuksekOyAlanParti = secimSonuclari.entries
              .reduce((a, b) => a.value > b.value ? a : b)
              .key;
          String markerIconPath = MarkerIconPaths.getIconPath(
              selectedElection!, enYuksekOyAlanParti);

          GeoPoint konum = data['genelBilgiler']['konum'];
          var markerId = MarkerId(doc.id);
          BitmapDescriptor icon = await getCustomIcon(markerIconPath);
          var marker = Marker(
              markerId: markerId,
              icon: icon,
              position: LatLng(konum.latitude, konum.longitude));
          markers.add(marker);
        }
      }

      emit(MahallelerLoaded(markers, elections,
          selectedElection: event.electionId));
    } catch (e) {
      if (kDebugMode) {
        print('Bir hata oluştu: $e');
      }
      emit(MahallelerError());
    }
  }

  Future<Map<String, String>> _fetchElections() async {
    DocumentSnapshot documentSnapshot = await FirebaseFirestore.instance
        .collection('secimler')
        .doc('secimler')
        .get();
    Map<String, dynamic>? data =
        documentSnapshot.data() as Map<String, dynamic>?;
    Map<String, String> elections = {};
    if (data != null && data['secimListesi'] != null) {
      Map<String, dynamic> secimListesi = data['secimListesi'];
      secimListesi.forEach((key, value) {
        elections[key] = value.toString();
      });
    }
    return elections;
  }

  Future<BitmapDescriptor> getCustomIcon(String path) async {
    final ByteData byteData = await rootBundle.load(path);
    final Uint8List bytes = byteData.buffer.asUint8List();
    return BitmapDescriptor.fromBytes(bytes);
  }
  
}
abstract class MahallelerEvent {}

class LoadMahalleler extends MahallelerEvent {}

class SelectElection extends MahallelerEvent {
  final String electionId;

  SelectElection(this.electionId);
}
class MahalleSecildi extends MahallelerEvent {
  final String mahalleId;
  final double latitude;
  final double longitude;

  MahalleSecildi({required this.mahalleId, required this.latitude, required this.longitude});
}

abstract class MahallelerState {}

class MahallelerInitial extends MahallelerState {}

class MahallelerLoaded extends MahallelerState {
  final Set<Marker> markers;
  final Map<String, String> elections;
  final String? selectedElection;

  MahallelerLoaded(this.markers, this.elections, {this.selectedElection});
}

class MahallelerError extends MahallelerState {}

I applied all the solutions I know but I failed

0

There are 0 answers