pushlio_flutter 0.1.11
pushlio_flutter: ^0.1.11 copied to clipboard
Pushlio push notification SDK — device registration, rich notifications, delivered/opened receipts, deep links, feature flags and in-app messages.
Changelog #
0.1.11 #
register: 8 saatlik TTL — ama SADECE payload değişmediyse #
Canlıda register her cold start'ta koşulsuzca ağa gidiyordu; ingest trafiğinin en büyük payı buydu. Artık son gönderilen cihaz bilgisi (token/versiyon/dil/izin/kimlik — hepsi) BİREBİR AYNIYSA ve son gönderimden bu yana 8 saat geçmediyse ağa hiç gidilmiyor.
Kritik nokta: bu bir "8 saatte bir gönder" değil, "değişmediyse VE 8 saat
içindeyse atla" mantığı. Push token rotasyonu, versiyon güncellemesi, dil/
saat dilimi değişikliği gibi GERÇEK bir fark varsa TTL tamamen görmezden
gelinip hemen gönderiliyor — özellikle token değişince sunucuda ölü bir
token'ın saatlerce kalmaması için bu şart. 404 device_not_found
kurtarma yolu da force: true ile bu korumayı atlıyor (yerel önbellek
"gönderildi" sansa bile sunucudaki satır silinmiş olabilir).
Panel etkisi: Cihaz listesindeki "son görülme" artık dakika değil,
en fazla 8 saat hassasiyetinde — aynı cihaz bilgisiyle art arda açılan
bir kullanıcı için last_seen bu pencere içinde tazelenmeyebilir. Bilinçli
bir denge: ürün kararı, hassasiyeti trafik tasarrufuna feda etti.
0.1.10 #
Remote config artık Pro plana özel #
syncConfig() her cihazda (Free dahil) ağa gidiyordu — flag/A-B testi
sistemi zaten ürün olarak Pro'ya özel niyetlenmişti ama SDK bunu hiç
zorlamıyordu. register yanıtı artık config_enabled (org planı) da
taşıyor; SDK bunu yerelde saklayıp Free planda syncConfig()'i ağa hiç
göndermeden atlıyor. Alan okunamazsa (eski sunucu, ayrıştırma sorunu)
güvenli varsayılan true — bir ayrıştırma hatası yüzünden Pro bir
müşterinin flag'leri sessizce kesilmez.
PushlioApi.register() artık bool yerine RegisterResult({ok, configEnabled})
dönüyor — bu tamamen iç bir sınıf (host uygulamalar hiç görmez), Pushlio
sınıfının genel API'sinde bir değişiklik yok.
0.1.9 #
syncConfig: 12 saatlik TTL cache #
Canlı log analizinde /ingest/config, register'dan sonra ingest'in en
sık çağrılan ikinci ucu çıktı — her cold start'ta (_warmup() içinde)
koşulsuzca ağa gidiyordu. Aynı gün art arda açılıp kapanan bir uygulamada
bu, flag'ler hiç değişmese bile her seferinde tam bir istek demekti.
Artık syncConfig() son başarılı çekimden bu yana 12 saat geçmediyse
ağa hiç gitmiyor, mevcut önbelleği kullanıyor (otomasyonlardaki "N saattir
aktif olmayan kullanıcı" eşikleriyle aynı mertebe; Firebase Remote
Config'in prod varsayılanı da 12 saat). Anında tazelik gerekiyorsa
(örn. bir "yenile" butonu) syncConfig(force: true) bu korumayı atlar.
Varsayılan davranış (parametresiz çağrı) geriye dönük uyumlu — sadece artık
gereksiz ağ isteği atmıyor.
0.1.8 #
login/logout: aynı kimlikle tekrar çağrılınca ağa gitmiyor #
Pushlio.login(id) koşulsuzca /ingest/user'a POST atıyordu — host
uygulama bunu her resume'da ya da her auth-state event'inde (örn. Firebase
Auth'un authStateChanges() akışı token yenilenince de tetikleniyor,
sadece gerçek login/logout'ta değil) aynı id ile çağırırsa, her seferinde
gereksiz bir istek gidiyordu. Canlıda bunun etkisini görmek kolay oldu:
/ingest/user tek başına registerden bile daha sık çağrılan uç oldu.
Artık login/logout idempotent: istenen kimlik zaten sunucuya
ulaşmışsa (PushlioIdentity.desired() == verilen id VE synced() true)
ağ isteği hiç atılmıyor. Kimlik gerçekten değiştiğinde ya da önceki
senkron başarısız kaldığında davranış aynı — sadece tekrarlanan no-op
çağrılar artık bedelsiz.
0.1.7 #
Eşzamanlı 404'te sessiz veri kaybı düzeltildi #
0.1.6'nın özyineleme koruması (_recovering bayrağı) yan etki getirmişti:
kurtarma sürerken 404 alan İKİNCİ çağrı beklemeden false dönüyordu.
setTags/setSubscribed kuyruksuz olduğundan bu kalıcı kayıptı — init
warmup'ı bitmeden login + setTags çağıran host'ta tag yutuluyordu.
Şimdi eşzamanlı 404'ler tek kurtarma Future'ını paylaşıyor: ikinci çağrı
kurtarmayı bekleyip kendi isteğini bir kez tekrar deniyor. Özyineleme
koruması bayraktan yapısal çözüme taşındı: kurtarma içindeki uzlaştırma
recover: false ile düz yola iner, kancaya dönecek yol hiç kalmaz.
0.1.7 #
androidLargeIcon: bildirim panelinde uygulama ikonu #
Durum çubuğu ikonu Android kuralı gereği tek renk siluet olmak zorunda — SystemUI siluet olmayan her şeyi düz gri lekeye çevirir. Markayı taşıyan RENKLİ uygulama ikonunun yeri panelin sağındaki BÜYÜK ikondur ve SDK bunu hiç desteklemiyordu: kampanya bildirimleri yalnızca soluk küçük ikonla çiziliyordu.
Pushlio.init(androidLargeIcon: '@mipmap/ic_launcher') bunu açar.
Varsayılan null'dur (davranış değişmez). Kampanya görseli olan bildirimde
görselin küçük hali kazanmaya devam eder — orada bilgi taşıyan odur.
Değer kalıcı konfigürasyona yazılır; uygulama kapalıyken arka plan
isolate'inin çizdiği bildirim de büyük ikonu alır.
0.1.6 #
Kurtarma yolunda sonsuz özyineleme kapatıldı #
0.1.5'te eklenen 404 kurtarması karşılıklı özyinelemeye açıktı:
onDeviceMissing → _register → bekleyen logout varsa
setExternalUserId(null) → o da _post'tan geçtiği için 404'te kancayı
YENİDEN tetikliyordu. register'ı _rawe bağlamak bu yolu kapatmıyordu.
Artık yeniden giriş bayrağı var.
0.1.5 #
Kullanıcı kimliği yarışı kapatıldı #
0.1.4'te init ağı beklemeyi bıraktı (açılışta 10-30 sn donmayı bu çözdü).
Ama kayıt arka plana alınınca şu yarış açıldı:
await Pushlio.init(...); // kayıt ARKA PLANDA başlar
await Pushlio.login(uid); // POST /user — cihaz satırı HENÜZ YOK
Sunucudaki ingest_set_external düz bir UPDATE'tir; satır yoksa 0 satır
günceller ve uç nokta 404 device_not_found döner. Bu hata yutuluyor,
kimlik hiçbir yere yazılmıyor ve kayıt onu göndermiyordu — yani cihaz-kullanıcı
bağlantısı KALICI olarak kayboluyordu. Oturumu açık kullanıcı her açılışta aynı
yarışı kaybettiği için cihaz hiçbir zaman bağlanmıyordu.
İki savunma hattı eklendi:
- Kimlik yerelde saklanır ve KAYIT PAYLOAD'UNDA taşınır. Sunucudaki
upsert
external_user_id = coalesce(excluded.external_user_id, devices.external_user_id)olduğu için sıra artık önemsiz: kayıt önce biterse POST /user zaten başarılı olur, login önce olursa kaydın kendisi kimliği taşır. Zamanlama hilesi değil, yapısal çözüm. - 404
device_not_foundalan istek kaydı zorlayıp BİR KEZ tekrar dener. Bu yarışın dışındaki sebepleri de toparlar (ör. cihaz panelden silinmiş). Ağ hatasıyla karıştırılmaz; kayıt da başarısızsa döngüye girilmez.
logout de artık kalıcı: ağ yoksa niyet saklanır ve bir sonraki başarılı
kayıttan sonra iletilir (kayıt payload'u null göndererek bağlantıyı
kaldıramaz — coalesce eskisini korur).
Aynı sorun setTag, trackEvent ve setSubscription çağrılarını da
vuruyordu; 404 kurtarması hepsini kapsıyor.
Soğuk açılışta çift ekran açılması giderildi #
onOtherNotificationTap artık SOĞUK AÇILIŞ dokunuşlarını host'a iletmiyor.
Yalnız KIRDIĞIMIZI telafi ederiz:
- sıcak yol (
onNewIntent→ tap handler):initialize()çağrımız host'un handler'ını EZER → geri vermek zorundayız. Değişmedi. - soğuk yol (
getNotificationAppLaunchDetails): pull API'dir,initialize'dan bağımsızdır — host'tan hiçbir şey almadık.
Soğuk yolda da iletmek, kendi getNotificationAppLaunchDetails() okumasını
yapan host'ta aynı dokunuşun İKİ kez işlenmesine yol açıyordu: hedef ekran
üst üste iki kez açılıyordu. O çağrı intent'i tüketmediği (saf okuma) için
SDK içinde dedupe yapmak imkânsız — host'un kendi okumasını bastıramayız.
getNotificationAppLaunchDetails() ÇAĞIRMAYAN uygulamalar bir yetenek
kaybetmez: flutter_local_notifications cold-start dokunuşunu zaten
onDidReceiveNotificationResponse'a hiç göndermez (eklentinin kendi
README'si: "This callback cannot be used to handle when a notification
launched an app"). O dokunuşa erişmenin tek yolu her zaman
getNotificationAppLaunchDetails() olmuştur.
test/host_isolation_test.dart bu ayrımı çiviliyor (SICAK iletilir, SOĞUK
iletilmez), test/identity_race_test.dart ise kimlik yarışının iki hattını.
0.1.4 #
Bu sürüm iki şeyi birden getiriyor: paketin kurulabilir olması ve host izolasyonu. İkincisi 0.1.4 olarak hazırlanmıştı ama hiç yayınlanmadı — çünkü paket o haliyle kurulamıyordu.
Kurulum engeli kaldırıldı (0.1.0-0.1.3'ün hepsini etkiliyordu) #
environment.sdk^3.11.3idi — bu, paketi geliştirme makinesinin Dart sürümüne kilitliyordu (flutter createdamgası olduğu gibi kalmıştı). Mart 2026'dan eski her Flutter'daflutter pub add pushlio_flutterÇÖZÜMLEME aşamasında reddediyordu: müşteri paketi kuramıyordu bile. Artık>=3.8.0 <4.0.0— alt uçtaki en katı bağımlılığın gerçek gereği (flutter_local_notifications 20.0.0 →^3.8.0).- Tabanı düşürmek körlemesine yapılamazdı: 6 yerde null-aware element
(
'k': ?x) kullanılıyordu ve o bir Dart 3.9 özelliği — tabanı tek başına yukarı çeken buydu. Klasikif (x != null) 'k': xbiçimine çevrildi.use_null_aware_elementslint'i bilinçli olarak kapatıldı (gerekçeanalysis_options.yamliçinde yazılı). device_info_plustabanı>=10.0.0→>=10.1.1. 10.0.0 pub.dev'de GERİ ÇEKİLMİŞ; ayrıca 10.0.xweb <=0.6.0isteyipfirebase_coreile çakışıyor — yani ilan edilen taban gerçekte hiç ulaşılamıyordu.path_provider>=2.1.4,shared_preferences>=2.3.0,http>=1.2.2: tabanlar birbiriyle tutarlı bir sete çekildi.
Host izolasyonu — Pushlio artık hiçbir koşulda host uygulamayı etkilemez #
Entegrasyon kütüphanesi olarak asıl sözleşmemiz bu; bu sürüm onu baştan sona uyguluyor.
initartık fırlatmaz. Önceden gövdesi korumasızdı: Firebase yapılandırılmamışsa,google-services.jsoneksikse ya da bir platform eklentisi hazır değilseawait Pushlio.init(...)istisna atıyordu.runApp()'ten önce çağıran uygulamalar hiç açılmıyordu.initartık ağ beklemez. Cihaz kaydı, makbuz kuyruğu ve flag tazeleme arka plana alındı. Önceden sunucu yavaşsa/erişilemezse açılış ~10-30 sn donabiliyordu (istek başına 10 sn timeout).- Hiçbir genel çağrı fırlatmaz.
login,logout,setTag(s),removeTag(s),trackEvent,setSubscription,requestPermission,inAppMessages,markInAppSeen,syncConfig,getDeviceUid,getToken— hepsi hata durumunda makul varsayılana döner. - Paylaşılan eklenti çakışması giderildi.
FlutterLocalNotificationsPluginbir SINGLETON'dır:initialize()çağrımız host'un tap handler'ını eziyordu. Uygulamanflutter_local_notificationskullanıyorsa kendi yerel bildirim dokunuşların sessizce çalışmaz oluyordu. Yeniinit(onOtherNotificationTap:)parametresiyle bize ait olmayan her dokunuş sana geri veriliyor (arka plan isolate'inde de çalışır). - Disk hataları izole edildi. SharedPreferences erişimi (makbuz/olay kuyruğu, bekleyen tıklama, flag önbelleği) korumalı; patlarsa makbuz kaybolur, uygulama etkilenmez.
- Sahipsiz async hata bırakılmıyor.
storePendingClick'teki.then()zincirinecatchErroreklendi; FCM dinleyicileri ve token rotasyonu korumalı sarmalayıcıdan geçiyor. - Arka plan mesaj handler'ı korumalı. Yabancı mesajın host handler'ına iletilmesi artık her şeyden önce yapılır: bizim tarafımızdaki bir arıza, senin kendi FCM mesajlarını almanı engelleyemez.
- Host callback'leri izole.
onClickhandler'ın patlarsa Pushlio yutar (stream ölmez, uygulama çökmez). - Yeni:
Pushlio.onError((e, st) {...})— yutulan hataları kendi crash raporlamana bağlamak için (opsiyonel). - Yeni:
Pushlio.isReady— yerel kurulum tamamlandı mı (yalnız teşhis). - Düzeltme:
enableLogs: trueiken init log'u değişken yerine literal$deviceUidSyncbasıyordu (kaçırılmış$).
Yeni: bildirim vurgu rengi #
Pushlio.init(androidAccentColor: 0xFF4CAF50)
- Renk ARGB int olarak verilir ve
AndroidNotificationDetails.colorüzerindenNotification.color'a gider. Android bunu bildirim gölgesindeki KÜÇÜK İKONA uygular; durum çubuğundaki ikon her koşulda monokrom kalır, bu yüzden küçük ikon beyaz siluet olmaya devam etmeli. - OneSignal'den geçenler için: bu,
NotificationAccentColor.DEFAULTmeta-data'sının beslediği slotun aynısıdır. - Renk
PushlioConfig'e kalıcı yazılır, yani uygulama TAMAMEN KAPALIYKEN gelen ve arka plan isolate'inin çizdiği bildirimlerde de uygulanır. - Verilmezse davranış değişmez. Eski sürümün yazdığı config okunmaya
devam eder (yeni anahtar yoksa renk
null).
Test ve korumalar #
test/host_isolation_test.dart— hiçbir platform eklentisinin olmadığı ve ağın kapalı olduğu ortamda tüm API'nin sorunsuz tamamlandığını doğrular. Koruma kaldırılırsa test kırmızıya döner.test/config_persistence_test.dart— ensure→load turu, kaldırma, eski config uyumu veapplyConfigaktarımı.scripts/check-flutter-floors.shyeniden yazıldı:^biçimini de görüyor, Dart tabanını denetliyor, geri çekilmiş sürüm arıyor ve en-kötü senaryoyupub downgradeile test ediyor (doğru yöntem bu; tek tek tam sürüm sabitlemek sahte hata üretiyordu).- Tüm korumalar artık CI'da koşuyor (
.github/workflows/sdk-checks.yml), ayrıca en eski desteklenen Flutter'da (3.32 / Dart 3.8) tam test.
0.1.3 #
Fixes a broken dependency floor — upgrade if you saw a build error.
flutter_local_notificationslower bound raised to>=20.0.0. 0.1.2 declared>=19.0.0, but the SDK calls the named-parameter API (initialize(settings: …),show(notificationDetails: …)) that only exists from 20.0.0 — apps resolving to 19.x failed to compile with "Too few positional arguments". Reported from a real integration.- Added
scripts/check-flutter-floors.sh(repo): compiles the package against each declared minimum so a wrong floor can't ship again —flutter analyzealone can't catch it, since it always resolves the newest compatible versions.
0.1.2 #
Pushlio.onForegroundMessage(handler)— intercept pushes while the app is in the foreground; returnfalseto suppress the notification and show your own UI (custom dialog etc.). Delivered receipts still flow.Pushlio.reportOpened(n)— count an "opened" when the user acts on your custom foreground UI.init(iosForegroundPresentation:)— set tofalseto stop the system from showing foreground alerts on iOS (pair with the handler above).
0.1.1 #
Compatibility release driven by real-world integration feedback.
- Widened dependency ranges so the SDK installs next to existing
Firebase/OneSignal setups:
firebase_core >=3.6 <5,firebase_messaging >=15.1 <17,flutter_local_notifications >=19 <23, and friends. (0.1.0 pinned the newest majors and broke resolution in older host apps.) init(onBackgroundMessage:)— firebase_messaging allows a single background handler; Pushlio now forwards every non-Pushlio message to your own top-level handler, so both systems coexist.init(androidSmallIcon:)— dedicated status-bar icon resource (launcher icons render as a flat square when tinted).init(androidChannelId/Name/Importance:)— channel is configurable (Android creates channels once; pick importance up front).init(enableLogs:)+Pushlio.deviceIdgetter for support matching.- Tests: foreign (non-
ph) messages are proven side-effect-free. - Server-side: device country now comes from the request IP (GeoIP at the ingest edge), not the device locale — no SDK change required.
0.1.0 #
Initial release.
- Push notifications: automatic device registration, rich notifications (big image + up to 3 action buttons), deep links with cold-start delivery, delivered/opened receipts with an offline-safe idempotent queue
- Identify & segment: external user id, tags, custom events, opt-out
- Feature flags & remote config with persistent cache
- In-app messages: fetch + seen-once marking