7.8 KiB
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<String> 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=linuxile 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çindewindowManager.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ı
AbsorbPointerveyaIgnorePointerile 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)
InteractiveViewerwidget'ıboundaryMargin: EdgeInsets.all(double.infinity)→ sınırsız kaydırmaminScale: 0.1,maxScale: 10.0onInteractionUpdate→ zoom oranını göstermek için (ops.)transformationControllerile programatik zoom kontrolü
- Mouse wheel event:
ListenerileonPointerSignalyakalaPointerScrollEvent→ 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)
ChangeNotifierile state yönetimi- Dışarıdan dosya eklendiğinde
notifyListeners()+precacheImage() ImageCache.maximumSizevemaximumSizeBytesayarı- 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<List<File>>widget'ı
Dosya: lib/services/image_manager.dart
Adım 6: Dosya Açma (Dialog + DragDrop)
file_pickerpaketi ileCtrl+OkısayoluDragTargetile sürükle-bırak- Desteklenen format filtreleri: PNG, JPEG, WebP, BMP, GIF
- Dikkat: Flutter Linux'ta DragTarget çalışıyor — ayrıca
window_manager'danonDragFileevent'i de var
Dosya: lib/services/file_handler.dart
Adım 7: Klavye Kısayolları
- Flutter'daki
Shortcuts+Actionssistemi ← →→ resim değiştirCtrl+O→ dosya açF11→ tam ekran (fullscreen toggle)Esc→ tam ekrandan çıkCtrl+Q→ çıkış
Dosya: lib/shortcuts/app_shortcuts.dart
Adım 8: Birleştirme ve Test
- Ana ekranı
ViewerScreen'de topla Listener(onPointerSignal)+InteractiveViewerentegrasyonuflutter run -d linuxile 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ı
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ı
DragToMoveAreaile 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.maximumSizeBytes1 GB'a çıkarılmalı RepaintBoundaryile sadece değişen bölgelerin yeniden çizilmesi sağlanırTexturebazlı 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 |