6.1 KiB
1. Temel Teknoloji Yığını ve Kütüphaneler
| Kategori | Kütüphane / Teknoloji | Neden Bu Tercih Edilmeli? |
|---|---|---|
| State Management | flutter_riverpod |
SOLID'in Dependency Inversion (D) prensibini uygulamak ve servisleri izole etmek için en modern ve güvenli yöntem. |
| Cihaz Keşfi (mDNS) | nsd veya bonsoir |
Zero-config ağ keşfi için endüstri standardı. TV kendini yayınlar, telefon bulur. |
| Ağ İletişimi | shelf (Local HTTP Server) |
TV tarafında raw TCP socket yerine küçük bir REST API (HTTP Server) kurmak hata yönetimini ve loglamayı inanılmaz kolaylaştırır. |
| Video Oynatıcı | media_kit |
Chewie/Video_player yerine kesinlikle bunu kullanmalısın. libmpv tabanlıdır. HLS (.m3u8), MKV ve özel codec'leri Android TV'de kusursuz oynatır. |
| Link Yakalama | receive_sharing_intent |
Mobil cihazda tarayıcıdan "Paylaş" aksiyonunu dinlemek için. |
| Lokal Depolama | shared_preferences |
Cihazın "TV" mi yoksa "Mobil" mi olduğunu ilk seçimden sonra hatırlamak için. |
2. Proje Klasör Yapısı (Clean Architecture)
Kodun test edilebilir ve modüler olması için Feature-First (Özellik Odaklı) bir Clean Architecture yapısı kurmalıyız:
lib/
├── core/ # Tüm projede ortak kullanılan yapılar
│ ├── constants/ # Port numaraları, API endpoint'leri
│ ├── error/ # Custom exception sınıfları
│ └── network/ # Temel ağ arayüzleri
├── features/
│ ├── setup/ # Cihaz rolü (TV/Telefon) seçim ekranı
│ ├── discovery/ # mDNS ile cihaz arama ve eşleşme (Mobil)
│ ├── receiver/ # TV tarafı: Local server ve oynatıcı
│ └── sender/ # Mobil taraf: Link yakalama ve gönderme
└── main.dart # Riverpod ProviderScope ve Init işlemleri
Her feature (özellik) kendi içinde şu üç katmana ayrılmalıdır (SOLID uygulamasının kalbi):
- domain: Sadece saf Dart kodları. Arayüzler (
abstract class), entity'ler. - data: Dış dünyayla iletişim. API çağrıları, paket implementasyonları.
- presentation: UI (Widget'lar) ve State (Riverpod Notifier'lar).
3. Kritik İş Akışları ve "Best Practice" Yöntemler
A. İlk Açılış ve Rol Seçimi (Setup)
Kullanıcı uygulamayı ilk açtığında karşısına iki dev buton çıkar: "Ben bir Telefonum (Gönderici)" ve "Ben bir TV'yim (Alıcı)".
- Best Practice: Bu seçim yapıldığı an
SharedPreferences'a kaydedilir (role = 'sender' | 'receiver'). Uygulamanın bir sonraki açılışındamain.dartbu değeri okur ve kullanıcıyı doğrudan ilgili ana ekrana yönlendirir.
B. Cihaz Eşleştirme (Discovery - mDNS)
Bluetooth yerine aynı ağda mDNS (Zero-conf) kullanmak en stabil "Best Practice" yöntemdir.
- TV (Receiver): Uygulama açılır açılmaz arka planda
nsdile bir servis kaydeder. Örnek servis adı:FastWatcher._http._tcp. Ayrıcashelfile 8080 portunda bir HTTP server dinlemeye başlar (POST /play). - Mobil (Sender): "Eşleş" butonuna basıldığında ağı tarar. Bulunan cihazların IP'sini bir listeye koyar. Kullanıcı TV'sine dokunduğunda IP adresi telefona kaydedilir.
C. İletişim Protokolü (HTTP üzerinden)
Raw Socket'ler veriyi bölebilir veya bağlantı kopmalarında sorun yaratabilir. TV'de mini bir HTTP sunucusu kurmak çok daha "Solid" bir çözümdür.
- Mobil'in Gönderdiği Payload (JSON):
{ "url": "https://site.com/master.m3u8", "title": "The Instigators (Opsiyonel)", "timestamp": 1714415045 } - TV'nin Yanıtı:
200 OK(Başarıyla aldım, oynatmaya başlıyorum).
4. SOLID Prensiplerinin Projeye Uygulanması
Bu projeyi "Solid" yapacak mimari kararlar şunlardır:
- S (Tek Sorumluluk):
DiscoveryServicesadece ağı tarar. UI'a karışmaz. Ekrana yazdırma işiniDiscoveryNotifier(State) veDiscoveryScreenyapar. - O (Açık/Kapalı): İletişim katmanını bir
abstract class NetworkClientolarak tanımla. Şu an Wi-Fi (HTTP) kullanıyoruz. Yarın Bluetooth eklemek istersen, mevcut kodu bozmadan yeni birBluetoothClientimplementasyonu ekleyebilirsin. - L (Liskov Yerine Geçme): Eğer TV tarafında birden fazla oynatıcı motoru kullanma ihtimalin varsa (örn:
MediaKitPlayerve yedek olarakExoPlayer), ikisi de birabstract class AppVideoPlayerarayüzünden türemelidir. - I (Arayüz Ayrımı): Sender ve Receiver çok farklı işler yapar. Her iki rol için devasa tek bir arayüz yazmak yerine,
ILinkSenderveILinkReceiverolarak küçük arayüzlere böl. - D (Bağımlılıkların Tersine Çevrilmesi): UI sınıfların (Widget'lar) asla doğrudan
nsdveyashelfpaketini bilmemelidir. Araya yazdığın soyut Repository sınıflarını (Riverpod üzerinden) UI'a enjekte etmelisin.
5. Geliştirme Adımları (Faz Planı)
Faz 1: Çekirdek ve İletişim (UI Olmadan)
- Proje yapısını ve Riverpod kurulumunu tamamla.
- TV tarafı için
shelfile basit bir HTTP server yaz ve Postman ile test et. - TV tarafında
nsdservisini başlat. - Mobil tarafta ağı tarayıp TV'nin IP'sini bulan kodu yaz.
Faz 2: Gönderim ve Oynatma
- Mobil cihazdan HTTP POST ile TV'ye manuel bir test linki (.m3u8) gönder.
- TV tarafında
media_kitpaketini kur ve gelen linki tam ekranda (kumanda odaklamalarına dikkat ederek) oynatan sayfayı tasarla.
Faz 3: Entegrasyon (Link Yakalama)
- Mobil tarafa
receive_sharing_intentpaketini kur. - Android Manifest ayarlarını yapıp, tarayıcıdan uygulamana link düşmesini sağla.
- Gelen linki ayrıştır (Parse) ve eşleşmiş olan TV'ye fırlat.
Bu mimariyle yazdığın bir kod hem çok hızlı çalışacak hem de aylar sonra dönüp baktığında nerede ne olduğunu saniyeler içinde bulabileceksin.
İlk olarak projeyi ayağa kaldırdıktan sonra, shelf ile TV tarafındaki o mini HTTP sunucusunun arayüzünü (Interface) tasarlayarak başlamak ister misin? Sana o kısmın Dependency Inversion'a uygun örnek kodunu yazabilirim.