Flutter & Mobil İle İleri Düzey Çoklu Cihaz Paylaşımlı Ajanda Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Geliştirme ortamınızın güncel olduğundan emin olmalısınız. Flutter 3.24 ve üzeri sürümler, performans iyileştirmeleri ve Dart 3.x'in sunduğu yeni özelliklerle (records, pattern matching) bu projeyi daha temiz kodlamanıza olanak tanır.

  • Flutter SDK: En son kararlı (stable) sürüm.
  • IDE: VS Code veya Android Studio (güncel eklentilerle).
  • Backend: Firebase Cloud Firestore (gerçek zamanlı veritabanı).
  • State Management: Riverpod veya Bloc (bu rehberde Riverpod kullanılacaktır).

Projenizi başlatırken flutter create shared_agenda komutunu kullanarak temel yapıyı oluşturun. Ardından pubspec.yaml dosyanıza cloud_firestore, firebase_core ve flutter_riverpod paketlerini ekleyerek bağımlılıkları yönetin.

Veri Modeli ve Güvenli Yapılandırma

Çoklu cihaz senkronizasyonunda en kritik nokta, verinin nasıl modellendiğidir. Ajanda öğeleriniz için tutarlı bir veri yapısı oluşturmak, ileride yaşanacak karmaşayı önler. Verilerinizi her zaman toJson ve fromJson metodları ile yönetin.


class AgendaItem {
  final String id;
  final String title;
  final DateTime dateTime;
  final String userId;

  AgendaItem({required this.id, required this.title, required this.dateTime, required this.userId});

  factory AgendaItem.fromJson(Map json) {
    return AgendaItem(
      id: json['id'],
      title: json['title'],
      dateTime: (json['dateTime'] as Timestamp).toDate(),
      userId: json['userId'],
    );
  }

  Map toJson() {
    return {
      'title': title,
      'dateTime': Timestamp.fromDate(dateTime),
      'userId': userId,
    };
  }
}

Bu model, veritabanı ile uygulama arasındaki köprüyü kurar. Timestamp kullanımı, farklı saat dilimlerine sahip cihazlarda veri tutarlılığını korumak için hayati önem taşır.

Gerçek Zamanlı Veri Senkronizasyonu Adım Adım

Firestore'un snapshots() metodunu kullanarak, veritabanındaki bir değişiklik olduğunda uygulamanın otomatik olarak güncellenmesini sağlayabilirsiniz. Bu, "çoklu cihaz paylaşımlı" özelliğinin temelidir.


Stream getAgendaStream(String userId) {
  return FirebaseFirestore.instance
      .collection('agenda')
      .where('userId', isEqualTo: userId)
      .snapshots()
      .map((snapshot) => snapshot.docs
          .map((doc) => AgendaItem.fromJson(doc.data()))
          .toList());
}

Bu fonksiyon, seçilen kullanıcıya ait ajanda verilerini sürekli dinler. Veritabanında bir kayıt eklendiğinde, silindiğinde veya güncellendiğinde UI (kullanıcı arayüzü) anında tepki verir.

State Management ile Veri Akışını Yönetme

Riverpod kullanarak veriyi uygulama genelinde erişilebilir kılmak, performans açısından en iyi yöntemdir. StreamProvider, Firebase'den gelen akışı otomatik olarak yönetir.


final agendaProvider = StreamProvider.family((ref, userId) {
  return getAgendaStream(userId);
});

Bu yaklaşım, kullanıcı oturumu kapattığında veya sayfa değiştiğinde akışın otomatik olarak temizlenmesini sağlar. Bellek sızıntılarını (memory leaks) önlemek için family modifier'ını kullanmak oldukça önemlidir.

Çoklu Cihaz Senkronizasyonunda Dikkat Edilmesi Gerekenler

Yöntem Avantajı Dezavantajı
Firestore Realtime Hızlı, düşük gecikme Çevrimdışı veri çakışmaları
Local Cache Çevrimdışı çalışma Karmaşık senkronizasyon mantığı

Çevrimdışı çalışma modu, Flutter'da FirebaseFirestore.instance.settings = const Settings(persistenceEnabled: true); satırı ile etkinleştirilir. Ancak, iki cihaz aynı anda aynı veriyi güncellerse "son yazan kazanır" (last-write-wins) stratejisi uygulanır. Kritik verilerde transaction kullanmayı ihmal etmeyin.

Güvenlik Uyarısı: Firestore güvenlik kurallarını (Security Rules) asla ihmal etmeyin. Kullanıcıların yalnızca kendi verilerine erişebildiğinden emin olmak için allow read, write: if request.auth.uid == userId; kuralını uygulayın.

Performans ve Hata Ayıklama İpuçları

Uygulamanız büyüdükçe, gereksiz okumaları engellemek için limit() ve orderBy() gibi sorgu kısıtlayıcılarını kullanın. Ayrıca, ListView.builder kullanarak sadece ekranda görünen öğelerin render edilmesini sağlayın.


ListView.builder(
  itemCount: agendaList.length,
  itemBuilder: (context, index) {
    final item = agendaList[index];
    return ListTile(
      title: Text(item.title),
      subtitle: Text(item.dateTime.toString()),
    );
  },
)

Hata ayıklama sırasında, Firestore'un debugPrint ile gelen loglarını takip edin. Eğer veri senkronize olmuyorsa, cihazın internet bağlantısını ve Firebase projesinin SHA-1 parmak izlerinin doğru tanımlanıp tanımlanmadığını kontrol edin.

Sıkça Sorulan Sorular

Firestore yerine neden kendi backend'imi kurmuyorum?

Kendi backend'inizi kurmak daha fazla kontrol sağlar ancak gerçek zamanlı senkronizasyon, ölçekleme ve güvenlik güncellemeleri gibi konularda Firebase, Flutter ile entegre bir şekilde çok daha hızlı sonuç verir.

Çevrimdışı modda veri çakışmalarını nasıl engellerim?

Firebase'in sunduğu transaction ve batch write yapılarını kullanarak atomik işlemler yapın. Bu, verinin ya tamamen güncellenmesini ya da hiç güncellenmemesini sağlar.

Birden fazla cihazda bildirimleri nasıl senkronize ederim?

Firebase Cloud Messaging (FCM) kullanarak, bir cihazda eklenen veriyi diğer cihazlara "push notification" olarak tetikleyebilirsiniz.

Veri güvenliği için şifreleme gerekli mi?

Firestore verileri sunucuda şifreli saklar. Ancak hassas kişisel veriler için istemci tarafında (AES gibi) ek şifreleme katmanı ekleyebilirsiniz.

Uygulama boyutu çok artarsa ne yapmalıyım?

Gereksiz paketleri kaldırın, resimleri optimize edin ve flutter build appbundle --split-per-abi komutu ile uygulama boyutunu minimize edin.

Yasal Uyarı: Bu rehber yazılım geliştirme tekniklerini öğretmeyi amaçlar. Uygulamanızda sakladığınız verilerin gizliliği ve güvenliği tamamen geliştiricinin sorumluluğundadır. KVKK ve GDPR uyumluluğu için gerekli yasal süreçleri takip edin.

İleri Düzey Senkronizasyon İçin İyimser UI (Optimistic UI) Uygulaması

Gerçek zamanlı ajanda uygulamalarında en büyük kullanıcı deneyimi sorunu, ağ gecikmeleri nedeniyle verinin ekrana yansımasının gecikmesidir. İyimser UI yaklaşımı, kullanıcı bir işlem yaptığında (örneğin yeni bir görev eklediğinde) sunucudan onay beklemeden arayüzü anında günceller. Eğer sunucu tarafında bir hata oluşursa, değişiklik geri alınır.

Riverpod kullanarak bu süreci nasıl yöneteceğinize dair temel bir yapı:


Future addTaskOptimistically(Task task) async {
  // 1. Yerel durumu anında güncelle
  ref.read(taskProvider.notifier).update((state) => [...state, task]);

  try {
    // 2. Sunucuya gönder
    await firestore.collection('tasks').add(task.toMap());
  } catch (e) {
    // 3. Hata durumunda yerel değişikliği geri al
    ref.read(taskProvider.notifier).update((state) => 
      state.where((t) => t.id != task.id).toList());
    
    // Kullanıcıya hata bildirimi yap
    showErrorSnackBar("Senkronizasyon başarısız oldu.");
  }
}

Uygulamanın Ölçeklenebilirliği İçin İleri Test Stratejileri

Çoklu cihaz senkronizasyonu içeren projelerde, sadece birim testleri (unit tests) yeterli değildir. Cihazlar arası veri tutarlılığını doğrulamak için entegrasyon testlerine odaklanmalısınız. Flutter'da integration_test paketini kullanarak, iki farklı emülatörün aynı anda veriyi nasıl işlediğini simüle edebilirsiniz.

Entegrasyon Testi Senaryosu

  1. Kurulum: İki farklı emülatörde aynı kullanıcı hesabı ile oturum açılır.
  2. Eylem: Birinci cihazda bir görev oluşturulur.
  3. Doğrulama: İkinci cihazda StreamBuilder üzerinden verinin 2 saniye içerisinde güncellendiği kontrol edilir.

testWidgets('İki cihaz arası senkronizasyon testi', (tester) async {
  await tester.pumpWidget(const MyApp());
  
  // Görev ekleme simülasyonu
  await tester.enterText(find.byType(TextField), 'Yeni Görev');
  await tester.tap(find.byType(ElevatedButton));
  
  // Veritabanı akışının güncellenmesini bekle
  await tester.pumpAndSettle(const Duration(seconds: 3));
  
  // Verinin ekranda göründüğünü doğrula
  expect(find.text('Yeni Görev'), findsOneWidget);
});

Performans İzleme İpuçları

  • Firestore Okuma Sayısı: where sorgularınızı optimize ederek gereksiz doküman okumalarını engelleyin.
  • Bellek Yönetimi: Dinleyici (listener) olarak açtığınız stream'leri, dispose metodunda mutlaka kapatın.
  • Görsel Varlıklar: Ajanda içerisinde kullanılan ikon ve görsel boyutlarını flutter_svg ile optimize edin.
Not: Uygulamanızın ölçeklenmesi durumunda, Firestore'un "index" yapısını kontrol etmeyi unutmayın. Karmaşık sorgular için Firebase konsolu üzerinden özel index'ler oluşturmanız gerekebilir.

Sonuç

Flutter & Mobil ile ileri düzey çoklu cihaz paylaşımlı ajanda yapmak, doğru mimari araçları seçtiğinizde oldukça keyifli ve öğretici bir süreçtir. Bu rehberde öğrendiğiniz Stream tabanlı veri akışı ve Riverpod ile durum yönetimi, gelecekteki tüm gerçek zamanlı uygulama projelerinizin temelini oluşturacaktır. Bir sonraki adım olarak, uygulamanıza "takım çalışması" özelliği ekleyerek verilerin farklı kullanıcılar arasında nasıl paylaşılabileceğini (Firestore sub-collections kullanarak) araştırmanızı öneririm.

Bu yazıya tepkinizi paylaşın:
Kerem Tekin

Teknik rehberler ve dijital araçların verimli kullanımı konusunda uzmanlaşmış bir editörüm. Karmaşık teknolojik süreçleri, herkesin kolayca uygulayabileceği rehberlere dönüştürerek zaman tasarrufu sağlamayı hedefliyorum.

Yorumlar (0)

Yorum Yaz