↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 9 — Konu 42: Kamera, Konum, Sensörler

3 dk okuma #flutter
Dizi · 42/61 Flutter Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
  3. Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
  4. Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
  5. Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
  6. Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
  7. Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
  8. Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
  9. Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
  10. Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
  11. Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
  12. Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
  13. Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
  14. Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
  15. Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
  16. Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
  17. Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
  18. Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
  19. Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
  20. Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
  21. Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
  22. Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
  23. Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
  24. Bölüm 6 — Konu 24: Provider Paketi
  25. Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
  26. Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
  27. Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
  28. Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
  29. Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
  30. Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
  31. Bölüm 7 — Konu 31: Deep Linking
  32. Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
  33. Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
  34. Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
  35. Bölüm 8 — Konu 35: Implicit Animasyonlar
  36. Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
  37. Bölüm 8 — Konu 37: Hero Animasyonları
  38. Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
  39. Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
  40. Bölüm 9 — Konu 40: Platform Channels
  41. Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
  42. Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
  43. Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
  44. Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
  45. Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
  46. Bölüm 10 — Konu 46: CI/CD
  47. Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
  48. Bölüm 10 — Konu 48: DevTools Profiling
  49. Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
  50. Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
  51. Bölüm 11 — Konu 51: Supabase Alternatifi
  52. Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
  53. Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
  54. Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
  55. Bölüm 12 — Konu 55: Flutter Web / Desktop
  56. Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
  57. Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
  58. Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
  59. Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
  60. Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
  61. Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
Dizinin sayfası →
İçindekiler 8 başlık
  1. Kamera — image_picker ve camera
  2. Basit Kullanım — image_picker
  3. İleri Kullanım — camera Paketi
  4. Konum — geolocator
  5. Sensörler — sensors_plus
  6. Üç Paketin Ortak Deseni
  7. 🎯 Bu Dersten Çıkarılması Gerekenler
  8. 📝 Ödevler

Konu 41'de öğrendiğimiz izin yönetimi, bu dersteki her özelliğin ön koşuludur — kamera, konum ya da sensör verisine erişmeden önce, her zaman ilgili izni kontrol/istemen gerekecek. Bu derste üç yaygın donanım erişimini, hazır paketlerle nasıl kullanacağını göreceğiz.

Kamera — image_picker ve camera

Kamerayla çalışmanın iki farklı seviyesi var, ihtiyacına göre seçersin:

Basit Kullanım — image_picker

Sadece kullanıcının bir fotoğraf çekmesini ya da galeriden seçmesini istiyorsan, kendi kamera arayüzünü yazmana gerek yok:

bash
flutter pub add image_picker
dart
import 'package:image_picker/image_picker.dart';

Future<void> fotografCek() async {
  final picker = ImagePicker();
  final XFile? fotograf = await picker.pickImage(
    source: ImageSource.camera, // veya ImageSource.gallery
    maxWidth: 1024, // büyük dosyaları küçültür
    imageQuality: 85, // 0-100 arası sıkıştırma
  );

  if (fotograf != null) {
    setState(() {
      _secilenDosya = File(fotograf.path); // dart:io — Dart Bölüm 13'ü hatırla
    });
  }
}

ImagePicker, cihazın kendi native kamera uygulamasını (Android'in kamera app'i, iOS'un kamera app'i) açar — sen kendi kamera arayüzünü yazmıyorsun, sistemin hazır olanını kullanıyorsun. XFile?, Dart Bölüm 2'de öğrendiğimiz nullable tip — kullanıcı iptal ederse null döner, bu yüzden if (fotograf != null) kontrolü zorunludur.

İleri Kullanım — camera Paketi

Kendi özelleştirilmiş kamera ekranını yapmak istiyorsan (örneğin bir Instagram-tarzı kayıt ekranı, canlı filtre önizlemesi), camera paketi doğrudan kamera akışına erişim verir:

bash
flutter pub add camera
dart
Future<void> kameraBaslat() async {
  final kameralar = await availableCameras(); // cihazdaki tüm kameraları listele
  final controller = CameraController(
    kameralar.first, // genelde arka kamera
    ResolutionPreset.medium,
  );
  await controller.initialize();
  // artık controller ile CameraPreview widget'ı gösterilebilir
}
dart
CameraPreview(controller) // canlı kamera görüntüsünü widget olarak göster

Ne zaman hangisini seçersin? Basit bir "profil fotoğrafı çek" özelliği için image_picker fazlasıyla yeterli ve daha az kod gerektirir. Kendi kamera arayüzünü tasarlaman, canlı önizleme üzerinde işlem yapman gerekiyorsa camera paketine geçersin — bu, Bölüm 6 Konu 28'de Provider/Riverpod/BLoC karşılaştırmasında gördüğümüz "ihtiyaca göre araç seçimi" mantığının aynısı.

Konum — geolocator

bash
flutter pub add geolocator
dart
import 'package:geolocator/geolocator.dart';

Future<Position?> konumAl() async {
  // 1. Konum servisi (GPS) açık mı?
  final servisAcikMi = await Geolocator.isLocationServiceEnabled();
  if (!servisAcikMi) {
    print('Kullanıcının GPS\'i kapalı');
    return null;
  }

  // 2. İzin durumu (Konu 41'i hatırla — geolocator kendi izin API'sini sunar)
  LocationPermission izin = await Geolocator.checkPermission();
  if (izin == LocationPermission.denied) {
    izin = await Geolocator.requestPermission();
    if (izin == LocationPermission.denied) {
      return null;
    }
  }
  if (izin == LocationPermission.deniedForever) {
    return null; // Konu 41'deki permanentlyDenied ile aynı fikir
  }

  // 3. Artık konumu güvenle alabiliriz
  return await Geolocator.getCurrentPosition(
    desiredAccuracy: LocationAccuracy.high,
  );
}

Dikkat et: geolocator, kendi izin API'sini (LocationPermission enum'u) sunuyor — bunun Konu 41'deki permission_handler'ın PermissionStatus'una kavramsal olarak birebir paralel olduğunu görüyorsun (denied/deniedForever, Konu 41'deki denied/permanentlyDenied'a karşılık geliyor). Pratikte genelde ikisinden birini tutarlı şekilde kullanırsın — geolocator'ın kendi izin sistemi, konum için tek başına yeterlidir, permission_handler'ı ayrıca eklemene gerek kalmaz.

Tek seferlik konum yerine sürekli takip:

dart
StreamSubscription<Position>? _konumAbonesi;

void konumTakibiBaslat() {
  _konumAbonesi = Geolocator.getPositionStream(
    locationSettings: const LocationSettings(
      accuracy: LocationAccuracy.high,
      distanceFilter: 10, // sadece 10 metreden fazla hareket edince güncelle
    ),
  ).listen((Position pozisyon) {
    print('Yeni konum: ${pozisyon.latitude}, ${pozisyon.longitude}');
  });
}

@override
void dispose() {
  _konumAbonesi?.cancel(); // Konu 36'daki AnimationController.dispose() ile AYNI prensip!
  super.dispose();
}

getPositionStream(), Bölüm 5 Konu 19'da öğrendiğimiz Stream kavramının gerçek dünya kullanımı — konum sürekli değiştiği için, tek seferlik bir Future yerine akan bir veri kaynağı daha uygun. distanceFilter: 10, gereksiz güncellemeleri (pil tüketimini) azaltan bir optimizasyon. _konumAbonesi?.cancel()'ı dispose()'ta çağırmayı unutmak, Konu 36'da AnimationController.dispose()'u unutmakla aynı sınıf hatadır — widget kalksa bile dinleme arka planda devam eder.

Sensörler — sensors_plus

bash
flutter pub add sensors_plus
dart
import 'package:sensors_plus/sensors_plus.dart';

StreamSubscription<AccelerometerEvent>? _ivmeAbonesi;

void ivmeOlcerDinlemeBaslat() {
  _ivmeAbonesi = accelerometerEventStream().listen((AccelerometerEvent olay) {
    print('x: ${olay.x}, y: ${olay.y}, z: ${olay.z}');
  });
}

@override
void dispose() {
  _ivmeAbonesi?.cancel();
  super.dispose();
}

Desen tamamen tanıdık geliyor olmalı — sensors_plus, geolocator'daki Stream yaklaşımının aynısını ivmeölçer, jiroskop (gyroscopeEventStream()), manyetometre (magnetometerEventStream()) gibi diğer sensörler için de sunuyor. Bu paket, izin gerektirmez (ivmeölçer/jiroskop gibi hareket sensörleri, çoğu platformda izin gerektirmeyen "hassas olmayan" veri sayılır) — bu da onu konum ve kameraya göre kullanmaya başlaması daha kolay bir paket yapıyor.

Üç Paketin Ortak Deseni

Fark ettiysen, üçü de aynı iskeleti paylaşıyor:

  1. Gerekliyse izin kontrolü/isteme (Konu 41).
  2. Ya tek seferlik bir değer al (Future — getCurrentPosition, pickImage) ya da sürekli bir akışa abone ol (Stream — getPositionStream, accelerometerEventStream).
  3. Stream'e abone olduysan, dispose()'ta mutlaka cancel() et.

Bu üçünü bir kez kavradığında, Flutter ekosistemindeki düzinelerce benzer donanım-erişim paketini (mikrofon, Bluetooth, NFC) okurken aynı zihinsel modeli kolayca uygulayabilirsin.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Basit fotoğraf çekme/seçme için image_picker (native kamera uygulamasını açar) yeterlidir; özelleştirilmiş bir kamera arayüzü için camera paketi (doğrudan akış erişimi) kullanılır.
  • geolocator, kendi izin API'sini (LocationPermission) sunar — Konu 41'deki permission_handler mantığına paralel çalışır, konum için genelde tek başına yeterlidir.
  • Tek seferlik konum için getCurrentPosition() (Future); sürekli takip için getPositionStream() (Stream) kullanılır.
  • sensors_plus, ivmeölçer/jiroskop/manyetometre verisini Stream olarak sunar, genelde izin gerektirmez.
  • Ortak desen: izin kontrolü → Future veya Stream → (Stream ise) dispose()'ta cancel(). Bu deseni unutmak, Konu 36'daki AnimationController sızıntısıyla aynı sınıf hatadır.

📝 Ödevler

  • [ ] image_picker ile hem kameradan çekme hem galeriden seçme seçeneği sunan, seçilen resmi ekranda gösteren bir ekran yap.
  • [ ] geolocator ile kullanıcının mevcut konumunu (enlem/boylam) alıp ekranda gösteren, izin akışının tüm adımlarını (kontrol → iste → deniedForever durumunda ayarlara yönlendirme) doğru yöneten bir buton yap.
  • [ ] getPositionStream() ile, kullanıcı hareket ettikçe güncellenen bir "canlı konum" göstergesi yap; dispose()'ta aboneliği düzgünce iptal ettiğini doğrula.
  • [ ] sensors_plus ile ivmeölçer verisini canlı gösteren basit bir ekran yap (örn. telefonu eğdikçe x/y/z değerlerinin değiştiğini gözlemle).
  • [ ] Kendi cümlelerinle, "neden bir Stream aboneliğini dispose()'ta iptal etmemenin bir bellek sızıntısına yol açtığını" açıkla.

Sıradaki konu: Bölüm 9 — Konu 43: Push Notification (FCM — Firebase Cloud Messaging)