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
- Kurulum: İki farklı emülatörde aynı kullanıcı hesabı ile oturum açılır.
- Eylem: Birinci cihazda bir görev oluşturulur.
- 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ı:
wheresorgularınızı optimize ederek gereksiz doküman okumalarını engelleyin. - Bellek Yönetimi: Dinleyici (listener) olarak açtığınız stream'leri,
disposemetodunda mutlaka kapatın. - Görsel Varlıklar: Ajanda içerisinde kullanılan ikon ve görsel boyutlarını
flutter_svgile 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.


Yorumlar (0)
Yorum Yaz