Flutter & Mobil İle İleri Düzey İnteraktif Harita Kümeleme Sistemi Nasıl Yapılır?

Flutter & Mobil İle İleri Düzey İnteraktif Harita Kümeleme Sistemi Nasıl Yapılır?
Flutter & Mobil İle İleri Düzey İnteraktif Harita Kümeleme Sistemi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projeye başlamadan önce geliştirme ortamınızın güncel olduğundan emin olmalısınız. Flutter 3.x ve üzeri sürümler, performans iyileştirmeleri ve güncel paket desteği için zorunludur. Projenizde kullanacağımız temel paketler şunlardır:

  • google_maps_flutter: Google Haritalar'ın Flutter arayüzü.
  • fluster: Harita noktalarını gruplandırmak için kullanılan, performans odaklı kümeleme kütüphanesi.
  • geolocator: Kullanıcı konumunu yönetmek için.

Öncelikle pubspec.yaml dosyanıza şu bağımlılıkları ekleyerek terminal üzerinden flutter pub get komutunu çalıştırın.

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.9.0
  fluster: ^2.0.0
  geolocator: ^13.0.0

Adım 1: Harita Veri Modeli ve Kümeleme Mantığı

Kümeleme sisteminin temelinde, her bir harita noktasının (marker) bir koordinata ve benzersiz bir kimliğe sahip olması yatar. fluster kütüphanesi, verilerinizi işleyebilmek için belirli bir arayüzü uygulamanızı bekler. Aşağıdaki kod bloğu, harita noktalarınızı kümelenebilir hale getiren MapMarker sınıfını tanımlar.

import 'package:fluster/fluster.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapMarker extends Clusterable {
  final String id;
  final LatLng position;
  final String? title;

  MapMarker({
    required this.id,
    required this.position,
    this.title,
    super.isCluster = false,
    super.clusterId,
    super.pointsSize,
    super.childMarkerId,
  }) : super(
          latitude: position.latitude,
          longitude: position.longitude,
          markerId: id,
        );
}

Bu sınıf, Clusterable sınıfından türetilerek fluster motorunun nesnelerinizi tanımasını sağlar. isCluster değişkeni, o öğenin bir grup mu yoksa tekil bir nokta mı olduğunu belirler.

Adım 2: Fluster Motorunun Başlatılması ve Yönetimi

Verileri kümelemek için bir Fluster örneği oluşturmanız gerekir. Bu motor, harita yakınlaştırma (zoom) seviyesine göre hangi noktaların birleşeceğine karar verir. Aşağıdaki örnekte, verilerin nasıl işlendiğini ve kümeleme motorunun nasıl yapılandırıldığını görebilirsiniz.

final Fluster fluster = Fluster(
  minZoom: 0,
  maxZoom: 20,
  radius: 150, // Kümeleme yarıçapı
  extent: 2048,
  nodeSize: 64,
  points: myMarkers, // List
);

Buradaki radius değeri, noktaların ne kadar yakın olduğunda birleşeceğini belirler. 150 değeri, mobil ekranlar için genellikle ideal bir denge sağlar.

Adım 3: Harita İşaretçilerini (Markers) Oluşturma

Kümeleme motorundan dönen verileri, google_maps_flutter paketinin anlayabileceği Marker nesnelerine dönüştürmemiz gerekir. Bu işlem, harita her hareket ettiğinde veya yakınlaştırma seviyesi değiştiğinde tetiklenmelidir.

Set getMarkers(double zoomLevel) {
  return fluster.clusters([-180, -85, 180, 85], zoomLevel.toInt()).map((cluster) {
    if (cluster.isCluster!) {
      return Marker(
        markerId: MarkerId(cluster.clusterId.toString()),
        position: LatLng(cluster.latitude!, cluster.longitude!),
        icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange),
        infoWindow: InfoWindow(title: '${cluster.pointsSize} nokta'),
      );
    }
    return Marker(
      markerId: MarkerId(cluster.markerId!),
      position: LatLng(cluster.latitude!, cluster.longitude!),
    );
  }).toSet();
}

Bu fonksiyon, haritanın o anki zoomLevel değerini alır ve kümeleme motoruna gönderir. Eğer bir nokta küme ise turuncu bir işaretçi, tekil nokta ise varsayılan işaretçiyi döndürür.

Adım 4: Performans Karşılaştırması ve Yöntemler

Harita üzerinde binlerce veriyi yönetirken farklı stratejiler izlenebilir. Aşağıdaki tablo, yaygın yöntemlerin performans üzerindeki etkilerini özetler.

Yöntem Avantaj Dezavantaj
Tüm Noktaları Render Etme Basit uygulama Düşük performans, donma
Sunucu Tarafı Kümeleme Hafif cihaz yükü Gecikmeli tepki (API)
İstemci Tarafı (Fluster) Anlık tepki, çevrimdışı destek İşlemci kullanımı

Adım 5: Harita Etkileşimi ve Kamera Hareketleri

Harita kümeleme sisteminin interaktif olması için kamera hareketlerini dinlemelisiniz. onCameraIdle callback'i, kullanıcı haritayı hareket ettirmeyi bıraktığında kümelemeyi yeniden hesaplamak için en verimli yerdir.

GoogleMap(
  initialCameraPosition: _kGooglePlex,
  onCameraIdle: () {
    setState(() {
      _currentMarkers = getMarkers(_currentZoom);
    });
  },
  onCameraMove: (position) {
    _currentZoom = position.zoom;
  },
  markers: _currentMarkers,
)

Kullanıcı haritayı kaydırdığında onCameraMove ile zoom değerini güncelliyoruz, harita durduğunda ise onCameraIdle ile işaretçileri yeniden çiziyoruz.

Kritik Güvenlik Uyarısı: Harita API anahtarlarınızı (API Key) asla doğrudan kod içerisinde veya Git depolarında açık metin olarak saklamayın. Üretim ortamında .env dosyalarını kullanın ve Firebase veya Google Cloud Console üzerinden API anahtarınızı belirli paket isimleri ve SHA-1 parmak izleri ile kısıtlayın.

Sıkça Sorulan Sorular

Harita neden bazen boş görünüyor?

Genellikle kümeleme motoruna gönderilen koordinatların (bounding box) yanlış hesaplanmasından kaynaklanır. fluster'ın clusters metoduna gönderdiğiniz koordinat sınırlarının (min/max lat/lng) doğru olduğundan emin olun.

Çok fazla işaretçi varken uygulama donuyor, ne yapmalıyım?

İşaretçi görsellerini (BitmapDescriptor) her karede yeniden oluşturmayın. Bunları bir değişkende önbelleğe alın (cache) ve sadece referans verin.

Kümelemeye tıkladığımda yakınlaştırma nasıl yapılır?

GoogleMapController kullanarak animateCamera metodunu çağırın ve kümenin koordinatlarına CameraUpdate.newLatLngZoom ile odaklanın.

İnternet bağlantısı olmadan kümeleme çalışır mı?

Evet, fluster tamamen yerel (local) bir kütüphanedir. Verileriniz cihazda olduğu sürece internet olmadan da çalışır.

Özel kümeleme ikonlarını nasıl eklerim?

BitmapDescriptor.fromAssetImage kullanarak kendi tasarladığınız PNG dosyalarını küme ikonları olarak tanımlayabilirsiniz.

İleri Düzey İpucu: Kümeleme Algoritmasını Dinamik Olarak Özelleştirme

Varsayılan kümeleme mantığı genellikle mesafe bazlı çalışır. Ancak, kullanıcı deneyimini iyileştirmek için kümeleme eşiklerini (threshold) yakınlaştırma seviyesine göre dinamik olarak değiştirmek isteyebilirsiniz. Fluster kütüphanesi, minZoom ve maxZoom parametreleri ile bu esnekliği sağlar.

Aşağıdaki kod bloğu, harita yakınlaştırma seviyesi arttıkça kümeleme hassasiyetinin nasıl artırılacağını göstermektedir:

final fluster = Fluster(
  minZoom: 0,
  maxZoom: 20,
  radius: 150, // Kümeleme yarıçapı
  extent: 2048,
  nodeSize: 64,
  points: points,
  createCluster: (BaseCluster cluster, double lng, double lat) {
    return MapMarker(
      id: cluster.id.toString(),
      position: LatLng(lat, lng),
      isCluster: true,
      clusterId: cluster.id,
      pointsSize: cluster.pointsSize,
      childMarkerId: cluster.childMarkerId,
    );
  },
);

Bu yapılandırmada radius değerini 150 olarak belirlemek, daha geniş bir alandaki noktaların tek bir kümede toplanmasını sağlar. Eğer haritanızda çok yoğun veri varsa, bu değeri 100-200 aralığında tutarak haritanın "kalabalık" görünmesini engelleyebilirsiniz.

Performans Optimizasyonu: Bellek Yönetimi ve İşaretçi Yenileme

Harita üzerinde binlerce işaretçi (marker) ile çalışırken en büyük darboğaz, her kamera hareketinde tüm kümeleme hesaplamasının ana iş parçacığında (main thread) yapılmasıdır. Bu durum arayüzde "takılmalara" (jank) neden olur.

Performansı artırmak için şu stratejileri izlemelisiniz:

  • Isolate Kullanımı: Çok büyük veri setlerinde (10.000+ nokta), kümeleme hesaplamasını compute fonksiyonu veya Isolate kullanarak arka planda gerçekleştirin.
  • Debounce Uygulaması: Kamera hareketi bittikten sonra kümeleme hesaplamasını tetiklemek için bir Timer kullanarak 300-500ms gecikme ekleyin.
  • Görünür Alan Filtrelemesi: Sadece haritanın o anki görünür sınırları (LatLngBounds) içerisindeki noktaları işleyin.

Kamera hareketini optimize etmek için şu yöntemi kullanabilirsiniz:

void onCameraIdle() {
  // Kamera durduğunda kümelemeyi güncelle
  final newMarkers = fluster.clusters(
    [_mapBounds.southWest.latitude, _mapBounds.southWest.longitude, 
     _mapBounds.northEast.latitude, _mapBounds.northEast.longitude],
    _currentZoom.toInt(),
  );
  
  setState(() {
    _markers = newMarkers;
  });
}

Bu yöntem, kullanıcının haritayı kaydırdığı sırada değil, sadece durduğunda hesaplama yaparak işlemci üzerindeki yükü ciddi oranda azaltır. Ayrıca, Marker nesnelerinizde key parametresini kullanarak Flutter'ın gereksiz widget yeniden oluşturmalarını (rebuild) engellediğinizden emin olun.

Sonuç

Flutter ile interaktif harita kümeleme sistemi kurmak, uygulamanızın profesyonellik seviyesini doğrudan artırır. Bu rehberde öğrendiğiniz fluster entegrasyonu ve kamera yönetimi, büyük veri setlerini yönetilebilir ve kullanıcı dostu hale getirir. Bir sonraki adım olarak, kümelere tıklandığında altındaki noktaları listeleyen bir BottomSheet yapısı ekleyerek kullanıcı deneyimini daha da zenginleştirebilirsiniz.

Sorumluluk Reddi: Bu makaledeki kod örnekleri eğitim amaçlıdır. Üretim ortamına almadan önce tüm API anahtarlarını güvenli bir şekilde saklayın ve uygulamanızın veri gizliliği politikalarını (KVKK/GDPR) göz önünde bulundurarak kullanıcı konumu verilerini işleyin.

Bu yazıya tepkinizi paylaşın:
Deniz Aydın

On yıllık deneyimli bir içerik editörü olarak, karmaşık süreçleri herkesin anlayabileceği basit adımlara dönüştürmeyi seviyorum. Okuyucuların hayatını kolaylaştıracak pratik çözümler üretmek temel uzmanlık alanımdır.

Yorumlar (0)

Yorum Yaz