# Implementation — imajViewer > Teknik uygulama dokümanı > Versiyon: 1.0 --- ## 1. Mimari Genel Bakış ``` ┌──────────────────────────────────────────────────┐ │ Flutter App │ │ ┌────────────┐ ┌────────────────────────────┐ │ │ │ window_mgr │ │ ViewerScreen │ │ │ │ (frameless)│ │ ┌──────────────────────┐ │ │ │ └────────────┘ │ │ CustomTitleBar │ │ │ │ │ │ (minimize, close) │ │ │ │ │ ├──────────────────────┤ │ │ │ │ │ ImageCanvas │ │ │ │ │ │ (InteractiveViewer) │ │ │ │ │ │ - Scroll → Zoom │ │ │ │ │ │ - Drag → Pan │ │ │ │ │ └──────────────────────┘ │ │ │ └────────────────────────────┘ │ │ ┌────────────────────────────────────────────┐ │ │ │ ImageCacheManager │ │ │ │ - PrecacheImage │ │ │ │ - LRU eviction (> N images) │ │ │ │ - File → Memory → GPU Texture │ │ │ └────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────┘ ``` ## 2. Bileşen Ağacı (Widget Tree) ``` MaterialApp (dark theme) └── ViewerScreen (StatefulWidget) ├── Stack │ ├── InteractiveViewer (zoom + pan) │ │ └── RawImage (resim) │ ├── CustomTitleBar (Positioned: top-right) │ │ ├── IconButton (minimize) │ │ └── IconButton (close) │ └── (koyu arka plan) └── DragTarget (sürükle-bırak dosya alanı) ``` ## 3. Data Flow ``` Kullanıcı dosya bırakır / açar │ ▼ ViewerScreen.dragResult / openFileDialog │ ▼ ImageList (List filePaths) │ ▼ preloadImages() → ImageCache'e ekle │ ▼ Indexed, currentIndex değişince │ ▼ InteractiveViewer.builder → Image.file(path) │ ▼ Flutter ImageCache → GPU Texture → Render ``` ## 4. Adım Adım Uygulama ### Adım 1: Proje İskeleti - `flutter create imajViewer` - `--platforms=linux` ile oluştur - Temele dark tema, frameless window ekle **Dosyalar:** `main.dart`, `app.dart` ### Adım 2: window_manager Kurulumu - pubspec.yaml: `window_manager: ^0.4.3` - `main()` içinde `windowManager.ensureInitialized()` - `waitUntilReadyToShow` → frameless ayarları: - `setTitleBarStyle(TitleBarStyle.hidden)` - `setSize(Size(1280, 800))` - `setMinimumSize(Size(800, 600))` - `setBackgroundColor(Colors.transparent)` - Linux'ta `linux/my_app.cc`'de window_manager için ek ayar gerekebilir **Dosyalar:** `lib/main.dart`, `pubspec.yaml` ### Adım 3: CustomTitleBar Widget - Sağ üst köşede, sabit konumda - Siyah yarı saydam arka plan - minimize (—) ve kapat (X) butonları - **Püf nokta:** Butonların üzerine gelince InteractiveViewer zoom yapmamalı - `AbsorbPointer` veya `IgnorePointer` ile InteractiveViewer etkileşimini engelle - Pencere boyutlandırma ve sürükleme kenarlıkları (window_manager'in `setResizable` özelliği) **Dosya:** `lib/widgets/custom_title_bar.dart` ### Adım 4: ImageCanvas (Ana Görüntüleme) - `InteractiveViewer` widget'ı - `boundaryMargin: EdgeInsets.all(double.infinity)` → sınırsız kaydırma - `minScale: 0.1`, `maxScale: 10.0` - `onInteractionUpdate` → zoom oranını göstermek için (ops.) - `transformationController` ile programatik zoom kontrolü - Mouse wheel event: `Listener` ile `onPointerSignal` yakala - `PointerScrollEvent` → transformationController ile zoom - Fare imlecinin resim üzerindeki konumuna göre zoom odağı **Püf nokta:** `InteractiveViewer`'ın default scroll-to-zoom desteği vardır ama imleç odağında zoom için `TransformationController`'a manuel matrix hesaplaması yapmak gerekir. **Dosya:** `lib/widgets/image_canvas.dart` ### Adım 5: Resim Yönetimi (ImageManager) - `ChangeNotifier` ile state yönetimi - Dışarıdan dosya eklendiğinde `notifyListeners()` + `precacheImage()` - `ImageCache.maximumSize` ve `maximumSizeBytes` ayarı - **Performans stratejisi:** - Sadece görünen resim bellekte tutulur - 12+ resim için `ImageCache.maximumSizeBytes` = 512MB - Resim değiştirince önceki resmin cache'ini düşürmeye gerek yok (Flutter LRU) - Sürükle-bırak: `DragTarget>` widget'ı **Dosya:** `lib/services/image_manager.dart` ### Adım 6: Dosya Açma (Dialog + DragDrop) - `file_picker` paketi ile `Ctrl+O` kısayolu - `DragTarget` ile sürükle-bırak - Desteklenen format filtreleri: PNG, JPEG, WebP, BMP, GIF - **Dikkat:** Flutter Linux'ta DragTarget çalışıyor — ayrıca `window_manager`'dan `onDragFile` event'i de var **Dosya:** `lib/services/file_handler.dart` ### Adım 7: Klavye Kısayolları - Flutter'daki `Shortcuts` + `Actions` sistemi - `← →` → resim değiştir - `Ctrl+O` → dosya aç - `F11` → tam ekran (fullscreen toggle) - `Esc` → tam ekrandan çık - `Ctrl+Q` → çıkış **Dosya:** `lib/shortcuts/app_shortcuts.dart` ### Adım 8: Birleştirme ve Test - Ana ekranı `ViewerScreen`'de topla - `Listener(onPointerSignal)` + `InteractiveViewer` entegrasyonu - `flutter run -d linux` ile test - Performance profiling: `flutter run --profile` ### Adım 9: Derleme - `flutter build linux --release` - Binary: `build/linux/x64/release/bundle/imajViewer` ## 5. pubspec.yaml Bağımlılıkları ```yaml dependencies: flutter: sdk: flutter window_manager: ^0.4.3 file_picker: ^8.0.0 path: ^1.9.0 ``` ## 6. Önemli Dikkat Noktaları ### Zoom Odağı (Cursor-based Zoom) InteractiveViewer varsayılan zoom odağı merkezdir. İmleç odağında zoom için `TransformationController` ile manuel matrix çarpımı: ``` 1. Scroll event yakala (PointerScrollEvent) 2. İmlecin resim üzerindeki koordinatını hesapla 3. Matrix4.translationValues(-txt) * Matrix4.diagonal3(values(scale)) * Matrix4.translationValues(txt) ``` Detaylı implementasyon `image_canvas.dart`'da yapılacak. ### Frameless Window Sürükleme Window_manager zaten varsayılan olarak fare ile pencerenin herhangi bir yerinden sürüklenmesine izin vermez. Bunun için: - CustomTitleBar alanını `DragToMoveArea` ile sarmak - Veya `windowManager.startDragging()` çağırmak Biz `DragToMoveArea` widget'ını kullanacağız. ### Performans - Flutter'ın `ImageCache`'i varsayılan olarak 50 MB ve 1000 entry ile sınırlıdır - 12 adet 4K resim için `ImageCache.maximumSizeBytes` 1 GB'a çıkarılmalı - `RepaintBoundary` ile sadece değişen bölgelerin yeniden çizilmesi sağlanır - `Texture` bazlı render ile GPU'da işleme devam eder ## 7. Timeline | Adım | İş | Tahmini Süre | |---|---|---| | 1-2 | Proje iskeleti + window_manager | 15 dk | | 3 | CustomTitleBar | 15 dk | | 4 | ImageCanvas + zoom | 30 dk | | 5-6 | Resim yönetimi + dosya açma | 30 dk | | 7 | Kısayollar | 10 dk | | 8-9 | Birleştirme + build | 20 dk | | **Toplam** | | **~2 saat** |