Watcher'lar
Veritabanı değişikliklerine gerçek zamanlı tepki verin
Watcher'lar
Watcher'lar veritabanınızdaki değişikliklere abone olmanıza ve verimli şekilde tepki vermenize olanak tanır. Gerçek zamanlı UI güncellemeleri ve senkronizasyon işlemleri için idealdir.
Watcher'lar yalnızca bir transaction başarıyla commit edildikten ve hedef gerçekten değiştiğinde tetiklenir.
Genel Bakış
Şunları izleyebilirsiniz:
- Belirli nesneler - Tek bir nesne değiştiğinde bildirim alın
- Koleksiyonlar - Koleksiyondaki herhangi bir nesne değiştiğinde bildirim alın
- Sorgular - Sorgu sonuçları değiştiğinde bildirim alın
Nesne İzleme
ID'sine göre belirli bir nesneyi izleyin:
@collection
class User {
Id? id;
late String name;
late int age;
}// Güncellenmiş nesneyi al
Stream<User?> userStream = isar.users.watchObject(5);
userStream.listen((user) {
if (user == null) {
print('User deleted');
} else {
print('User changed: ${user.name}');
}
});
// Değişiklik tetikle
final user = User()..id = 5..name = 'David'..age = 25;
await isar.writeAsync((isar) => isar.users.put(user));
// Çıktı: User changed: David
user.name = 'Mark';
await isar.writeAsync((isar) => isar.users.put(user));
// Çıktı: User changed: Mark
await isar.writeAsync((isar) => isar.users.delete(5));
// Çıktı: User deleted// Sadece bildir, nesneyi alma
Stream<void> userStream = isar.users.watchObjectLazy(5);
userStream.listen((_) {
print('User 5 changed');
});
final user = User()..id = 5..name = 'David'..age = 25;
await isar.writeAsync((isar) => isar.users.put(user));
// Çıktı: User 5 changedNesnenin henüz var olması gerekmez. Oluşturulduğunda watcher sizi bilgilendirir.
Hemen Tetikle
Mevcut değeri anında alın:
Stream<User?> userStream = isar.users.watchObject(
5,
fireImmediately: true,
);
userStream.listen((user) {
print('User: ${user?.name}');
});
// Mevcut değeri (veya null) hemen yazarKoleksiyon İzleme
Bir koleksiyondaki tüm değişiklikleri izleyin:
// Sadece bildirim al
Stream<void> usersStream = isar.users.watchLazy();
usersStream.listen((_) {
print('A user changed');
});
await isar.writeAsync((isar) async {
isar.users.put(User()..name = 'Alice');
});
// Çıktı: A user changed// Her değişiklikte tüm nesneleri getir
Stream<List<User>> usersStream = isar.users.watch();
usersStream.listen((users) {
print('Users: ${users.map((u) => u.name).join(', ')}');
});
await isar.writeAsync((isar) async {
isar.users.put(User()..name = 'Alice');
});
// Çıktı: Users: AliceTam nesnelerle koleksiyon izlemek büyük koleksiyonlarda pahalı olabilir!
Sorgu İzleme
Belirli bir sorgunun sonuçlarını izleyin:
@collection
class User {
Id? id;
late String name;
late int age;
}// Sorgu oluştur
final adultsQuery = isar.users
.where()
.ageGreaterThan(18)
.build();
// Sorgu sonuçlarını izle
Stream<List<User>> adultsStream = adultsQuery.watch(
fireImmediately: true,
);
adultsStream.listen((adults) {
print('Adults: ${adults.map((u) => u.name).join(', ')}');
});
// Mevcut sonuçları hemen yazar
// Çocuk ekle (bildirim yok)
await isar.writeAsync((isar) async {
isar.users.put(User()..name = 'Child'..age = 10);
});
// Çıkış yok - sorguya uymuyor
// Yetişkin ekle (bildirim gelir)
await isar.writeAsync((isar) async {
isar.users.put(User()..name = 'Alice'..age = 25);
});
// Çıktı: Adults: Alice
// Başka yetişkin
await isar.writeAsync((isar) async {
isar.users.put(User()..name = 'Bob'..age = 30);
});
// Çıktı: Adults: Alice, BobSorgu watcher'ları yalnızca sonuçlar gerçekten değiştiğinde tetiklenir!
Lazy Sorgu İzleme
final adultsQuery = isar.users
.where()
.ageGreaterThan(18)
.build();
Stream<void> adultsStream = adultsQuery.watchLazy();
adultsStream.listen((_) {
print('Adult users changed');
});Sorgu Watcher Sınırlamaları
offset, limit veya distinct kullanırken, görünür sonuçlar değişmemiş olsa bile bildirim gelebilir.
// Fazladan bildirim olabilir
final topUsers = isar.users
.where()
.sortByAge()
.limit(10)
.build();
topUsers.watch().listen((users) {
// İlk 10 değişmemiş olsa da tetiklenebilir
});Gerçek Dünya Örnekleri
Flutter UI Güncellemeleri
class UserListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<List<User>>(
stream: isar.users.where().watch(fireImmediately: true),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return ListTile(
title: Text(user.name),
subtitle: Text('Age: ${user.age}'),
);
},
);
},
);
}
}Kullanıcı Profili
class UserProfileWidget extends StatelessWidget {
final int userId;
const UserProfileWidget({required this.userId});
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: isar.users.watchObject(userId, fireImmediately: true),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('User not found');
}
final user = snapshot.data!;
return Column(
children: [
Text('Name: ${user.name}'),
Text('Age: ${user.age}'),
],
);
},
);
}
}Arama Sonuçları
class SearchWidget extends StatefulWidget {
@override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
String searchQuery = '';
@override
Widget build(BuildContext context) {
final query = isar.users
.where()
.nameContains(searchQuery, caseSensitive: false)
.build();
return Column(
children: [
TextField(
onChanged: (value) {
setState(() {
searchQuery = value;
});
},
),
Expanded(
child: StreamBuilder<List<User>>(
stream: query.watch(fireImmediately: true),
builder: (context, snapshot) {
if (!snapshot.hasData) return SizedBox();
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(users[index].name),
);
},
);
},
),
),
],
);
}
}Sunucuya Senkronizasyon
class SyncService {
void startWatching() {
isar.users.watchLazy().listen((_) async {
await syncUsersToServer();
});
}
Future<void> syncUsersToServer() async {
final users = await isar.users.where().findAll();
// Sunucuya gönder...
}
}Cache Geçersiz Kılma
class CacheService {
final _cache = <int, User>{};
void startWatching() {
isar.users.watchLazy().listen((_) {
_cache.clear();
print('Cache invalidated');
});
}
Future<User?> getUser(int id) async {
if (_cache.containsKey(id)) {
return _cache[id];
}
final user = await isar.users.get(id);
if (user != null) {
_cache[id] = user;
}
return user;
}
}Performans Dikkatleri
// ✅ Veriye ihtiyacınız yoksa lazy watcher kullanın
isar.users.watchLazy().listen((_) {
// Sadece cache invalid et
needsRefresh = true;
});
// ✅ Tüm koleksiyon yerine spesifik sorguları izleyin
isar.users
.where()
.statusEqualTo('active')
.watch()
.listen((activeUsers) {
// Aktif kullanıcıları işle
});
// ✅ İşiniz bitince abonelikleri iptal edin
final subscription = isar.users.watchLazy().listen((_) {});
// Daha sonra...
subscription.cancel();// ❌ Büyük koleksiyonları tam veriyle izlemeyin
isar.users.watch().listen((allUsers) {
// Her değişiklikte TÜM kullanıcıları yeniden çeker
});
// ❌ Listener içinde ağır işlemler yapmayın
isar.users.watchLazy().listen((_) async {
// Ağır işlem
await processAllUsers();
});
// ❌ Abonelikleri iptal etmeyi unutmayın
isar.users.watchLazy().listen((_) {
// İptal etmezseniz sonsuza kadar çalışır
});StreamBuilder ile Kullanım
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<List<User>>(
stream: isar.users
.where()
.ageGreaterThan(18)
.build()
.watch(fireImmediately: true),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Text('No users found');
}
final users = snapshot.data!;
return ListView(
children: users.map((user) =>
ListTile(title: Text(user.name))
).toList(),
);
},
);
}
}En İyi Uygulamalar
- Sadece bildirim gerekiyorsa lazy watcher kullanın
- Tüm koleksiyon yerine spesifik sorguları izleyin
- Widget dispose olduğunda abonelikleri iptal edin
- Watcher callback'lerinde ağır işlemlerden kaçının
- İlk UI durumu için fireImmediately kullanın
Watcher'lar verimli ve hafiftir. Reaktif UI'lar oluşturmak için özgürce kullanın!
Sonraki Adımlar
Son Güncelleme