### 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: ```text 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ışında `main.dart` bu 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. 1. **TV (Receiver):** Uygulama açılır açılmaz arka planda `nsd` ile bir servis kaydeder. Örnek servis adı: `FastWatcher._http._tcp`. Ayrıca `shelf` ile 8080 portunda bir HTTP server dinlemeye başlar (`POST /play`). 2. **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):** ```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):** `DiscoveryService` sadece ağı tarar. UI'a karışmaz. Ekrana yazdırma işini `DiscoveryNotifier` (State) ve `DiscoveryScreen` yapar. * **O (Açık/Kapalı):** İletişim katmanını bir `abstract class NetworkClient` olarak tanımla. Şu an Wi-Fi (HTTP) kullanıyoruz. Yarın Bluetooth eklemek istersen, mevcut kodu bozmadan yeni bir `BluetoothClient` implementasyonu ekleyebilirsin. * **L (Liskov Yerine Geçme):** Eğer TV tarafında birden fazla oynatıcı motoru kullanma ihtimalin varsa (örn: `MediaKitPlayer` ve yedek olarak `ExoPlayer`), ikisi de bir `abstract class AppVideoPlayer` arayü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, `ILinkSender` ve `ILinkReceiver` olarak küçük arayüzlere böl. * **D (Bağımlılıkların Tersine Çevrilmesi):** UI sınıfların (Widget'lar) asla doğrudan `nsd` veya `shelf` paketini 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)** 1. Proje yapısını ve Riverpod kurulumunu tamamla. 2. TV tarafı için `shelf` ile basit bir HTTP server yaz ve Postman ile test et. 3. TV tarafında `nsd` servisini başlat. 4. Mobil tarafta ağı tarayıp TV'nin IP'sini bulan kodu yaz. **Faz 2: Gönderim ve Oynatma** 1. Mobil cihazdan HTTP POST ile TV'ye manuel bir test linki (.m3u8) gönder. 2. TV tarafında `media_kit` paketini kur ve gelen linki tam ekranda (kumanda odaklamalarına dikkat ederek) oynatan sayfayı tasarla. **Faz 3: Entegrasyon (Link Yakalama)** 1. Mobil tarafa `receive_sharing_intent` paketini kur. 2. Android Manifest ayarlarını yapıp, tarayıcıdan uygulamana link düşmesini sağla. 3. 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.