İçeriğe geç / Skip to content / Zum Inhalt

Flutter: Tarih ve Saat Seçiciler (DatePicker & TimePicker)

Ahmet Balaman

Son güncelleme:

5 dk okuma

FlutterDatePickerTimePickershowDatePickerDateTimeLocalization
Flutter: Tarih ve Saat Seçiciler (DatePicker & TimePicker)

Doğum tarihi, randevu saati, hatırlatma zamanı gibi bilgileri kullanıcıya elle yazdırmak hem yorucu hem hataya açıktır: biri "3/5/2026" yazar, diğeri "05.03.26". Flutter'ın yerleşik showDatePicker ve showTimePicker fonksiyonları bu sorunu çözer; Material tasarımlı bir takvim ya da saat dialog'u açar ve bize doğrudan DateTime ya da TimeOfDay nesnesi verir. İkisi de asenkron çalışır, yani sonucu await ile bekleriz.

Canlı Demo

Aşağıdaki interaktif örnekte bu widget'ları deneyebilirsiniz:

💡 Eğer yukarıdaki örnek açılmazsa, DartPad linkine tıklayarak yeni sekmede çalıştırabilirsiniz.

DatePicker (Tarih Seçici)

Bir takvim arayüzü açarak kullanıcının gün, ay ve yıl seçmesini sağlar.

Temel Kullanım

Fonksiyon Future<DateTime?> döndürür. Kullanıcı "İptal"e basarsa ya da dialog'un dışına dokunursa sonuç null gelir:

DateTime? selectedDate;

Future<void> _selectDate() async {
  final now = DateTime.now();

  final DateTime? picked = await showDatePicker(
    context: context,
    initialDate: selectedDate ?? now, // Takvim açıldığında seçili gelen tarih
    firstDate: DateTime(2000),        // Seçilebilecek en erken tarih
    lastDate: DateTime(now.year + 5), // Seçilebilecek en geç tarih
  );

  if (picked != null && picked != selectedDate) {
    setState(() {
      selectedDate = picked;
    });
  }
}

lastDate için sabit bir yıl yazmak yerine now.year + 5 gibi göreli bir değer kullandığıma dikkat edin; nedenini hatalar bölümünde anlatacağım. await konusu yeniyse önce async/await yazısına bakmak faydalı olur.

Bir butona basınca çağırmak için:

ElevatedButton(
  onPressed: _selectDate,
  child: Text(
    selectedDate == null
        ? 'Tarih Seç'
        : MaterialLocalizations.of(context).formatShortDate(selectedDate!),
  ),
)

Tarihi "${d.day}/${d.month}/${d.year}" diye elle birleştirmek yerine MaterialLocalizations kullanmak, ek paket gerektirmeden uygulamanın diline uygun bir yazım verir. Daha serbest biçimler gerektiğinde intl paketindeki DateFormat devreye girer.

Doğum Tarihi Gibi Eski Tarihler

Varsayılan takvim görünümünde otuz yıl geriye gitmek onlarca dokunuş demektir. İki parametre bunu çözer:

showDatePicker(
  context: context,
  initialDate: DateTime(2000),
  firstDate: DateTime(1920),
  lastDate: DateTime.now(),
  initialDatePickerMode: DatePickerMode.year,    // Önce yıl listesi açılsın
  initialEntryMode: DatePickerEntryMode.calendar, // .input verilirse klavyeyle yazılır
);

TimePicker (Saat Seçici)

Kullanıcının saat ve dakika seçmesini sağlar.

Temel Kullanım

Bu fonksiyon Future<TimeOfDay?> döndürür.

TimeOfDay selectedTime = TimeOfDay.now();

Future<void> _selectTime() async {
  final TimeOfDay? picked = await showTimePicker(
    context: context,
    initialTime: selectedTime,
    builder: (context, child) {
      return MediaQuery(
        // 24 saat formatı için zorlama (opsiyonel)
        data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
        child: child!,
      );
    },
  );

  if (picked != null && picked != selectedTime) {
    setState(() {
      selectedTime = picked;
    });
  }
}

TimeOfDay yalnızca saat ve dakika taşır; tarih bilgisi yoktur. Ekranda göstermek için selectedTime.format(context) yeterlidir. Tarih ve saati tek bir DateTime'da birleştirmeyi aşağıdaki senaryoda göreceğiz.

Yerelleştirme (Localization)

Takvimin Türkçe görünmesi için önce pubspec.yaml dosyasına Flutter'ın kendi yerelleştirme paketini ekleriz:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

Sonra MaterialApp'e delegeleri ve desteklenen dilleri veririz:

import 'package:flutter_localizations/flutter_localizations.dart';

MaterialApp(
  localizationsDelegates: const [
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: const [
    Locale('en'), // İngilizce
    Locale('tr'), // Türkçe
  ],
  // ...
)

Bunu yaptığımızda showDatePicker cihazın diline göre (ya da locale parametresiyle belirttiğimiz dile göre) gün ve ay adlarını, buton metinlerini ve haftanın ilk gününü kendisi ayarlar.

Tema Özelleştirme

Picker renklerini değiştirmek için builder parametresiyle dialog'u bir Theme ile sararız:

showDatePicker(
  context: context,
  // ... parametreler
  builder: (context, child) {
    final theme = Theme.of(context);
    return Theme(
      data: theme.copyWith(
        colorScheme: theme.colorScheme.copyWith(
          primary: Colors.red,     // Seçili gün ve başlık rengi
          onPrimary: Colors.white, // Seçili gün yazı rengi
        ),
      ),
      child: child!,
    );
  },
);

Mevcut colorScheme'i copyWith ile değiştirmek, sıfırdan ColorScheme.light(...) yazmaktan daha güvenlidir; böylece karanlık temada takvim birdenbire beyaza dönmez. Uygulamanın tamamında aynı görünüm isteniyorsa ThemeData içindeki datePickerTheme ve timePickerTheme alanları kullanılır.

Ne Zaman Kullanılır, Ne Zaman Kullanılmaz?

  • Kullan: tek bir gün ya da tek bir saat seçtirmek; kullanıcının takvime bakarak karar verdiği durumlar (randevu, teslim tarihi).
  • Kullanma: başlangıç-bitiş aralığı. İki ayrı picker açmak yerine tek seferde aralık seçtiren showDateRangePicker var.
  • Kullanma: takvimin sayfanın parçası olması gereken ekranlar. Dialog yerine gömülü takvim için CalendarDatePicker widget'ı doğrudan sayfaya yerleştirilir.
  • Kullanma: iOS'a özgü görünümün önemli olduğu uygulamalar. Tekerlek tarzı seçim için CupertinoDatePicker kullanılır; genellikle bir alt sayfa (bottom sheet) içinde gösterilir.
  • Kullanma: kredi kartı son kullanma tarihi gibi ay/yıl girişleri. Burada takvim açmak yerine biçimlendirilmiş bir metin alanı ya da iki kısa dropdown daha hızlıdır.

Seçilen tarihi bir form alanında göstermek gerekiyorsa readOnly: true verilmiş bir TextFormField'ın onTap'inde picker'ı açmak yaygın ve kullanışlı bir desendir; böylece alan Form doğrulamasına da katılır.

Sık Yapılan Hatalar

1. initialDate'in aralık dışında kalması

Belirti: Picker açılmaz; konsolda "initialDate … must be on or before lastDate …" (ya da "on or after firstDate") assertion hatası görünür.

Sebep: Çoğu zaman sabit yazılmış bir yıl. lastDate: DateTime(2025) yazılan kod o yıl sorunsuz çalışır, takvim 2026'ya döndüğünde initialDate: DateTime.now() aralığın dışında kalır ve uygulama bir sabah kendiliğinden bozulur.

Çözüm: Sınırları DateTime.now()'a göre hesaplamak ve kayıtlı bir tarihi initialDate yapmadan önce aralığın içinde olduğundan emin olmak.

2. await sonrasında context kullanmak

Belirti: Önce tarih, hemen ardından saat seçtirirken ikinci çağrıda use_build_context_synchronously uyarısı ("Don't use 'BuildContext's across async gaps").

Sebep: Kullanıcı takvim açıkken sayfadan çıkmış olabilir; await bittiğinde elimizdeki context artık geçerli olmayabilir. Aynı durum AlertDialog için de geçerlidir, çünkü picker'lar da özünde birer dialog'dur.

Çözüm: Her await'ten sonra kontrol etmek:

final date = await showDatePicker(/* ... */);
if (date == null || !context.mounted) return;
final time = await showTimePicker(context: context, initialTime: TimeOfDay.now());

3. Takvimin İngilizce kalması

Belirti: Uygulama Türkçe ama takvimde "MON, TUE", "CANCEL" yazıyor.

Çözüm: flutter_localizations paketini ekleyip yukarıdaki delegeleri ve supportedLocales listesini tanımlamak. Delegeler olmadan yalnızca locale: const Locale('tr') vermek işe yaramaz.

4. Tarihin sunucuda bir gün geriye kayması

Belirti: Kullanıcı 15 Mayıs'ı seçer, veritabanında 14 Mayıs görünür.

Sebep: showDatePicker yerel saate göre gece yarısını döndürür. Bunu picked.toUtc().toIso8601String() ile gönderirsek, UTC'nin ilerisindeki bir saat diliminde (örneğin Türkiye) gece yarısı, UTC'de bir önceki günün akşamına denk gelir.

Çözüm: Doğum günü gibi saatten bağımsız tarihleri yalnızca tarih olarak göndermek:

String toDateOnly(DateTime d) =>
    '${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';

Mini Senaryo: Hafta İçi Randevu Seçimi

Bir randevu ekranı düşünelim. Kurallar şöyle: bugünden itibaren 30 gün içinde, yalnızca hafta içi bir gün seçilebilir; ardından saat sorulur ve ikisi tek bir DateTime'da birleştirilir.

DateTime? _appointment;

DateTime _nextWeekday(DateTime day) {
  var d = DateUtils.dateOnly(day);
  while (d.weekday > DateTime.friday) {
    d = DateUtils.addDaysToDate(d, 1);
  }
  return d;
}

Future<void> _pickAppointment() async {
  final today = DateUtils.dateOnly(DateTime.now());

  final date = await showDatePicker(
    context: context,
    initialDate: _nextWeekday(today),
    firstDate: today,
    lastDate: DateUtils.addDaysToDate(today, 30),
    selectableDayPredicate: (day) => day.weekday <= DateTime.friday,
    helpText: 'Randevu günü',
  );
  if (date == null || !mounted) return;

  final time = await showTimePicker(
    context: context,
    initialTime: const TimeOfDay(hour: 10, minute: 0),
    helpText: 'Randevu saati',
  );
  if (time == null || !mounted) return;

  setState(() {
    _appointment = DateTime(
      date.year, date.month, date.day, time.hour, time.minute,
    );
  });
}

selectableDayPredicate hafta sonlarını takvimde soluk ve tıklanamaz yapar. Burada gözden kaçan bir ayrıntı var: initialDate de bu kurala uymak zorunda. Bugün cumartesiyse ve initialDate: today verirsek picker assertion hatasıyla açılmaz. _nextWeekday fonksiyonu bu yüzden var; başlangıç tarihini ilk uygun güne kaydırıyor. Gün eklerken Duration(days: 1) yerine DateUtils.addDaysToDate kullanmak da yaz saati geçişlerinde saatin kaymasını önler.

Kullanıcı iki adımdan herhangi birinde vazgeçerse null döner ve hiçbir şey değişmez; yarım kalmış bir randevu durumu oluşmaz. Sonucu ekranda göstermek için yine MaterialLocalizations yeterli:

final loc = MaterialLocalizations.of(context);
final label = _appointment == null
    ? 'Randevu seç'
    : '${loc.formatFullDate(_appointment!)} · '
      '${loc.formatTimeOfDay(TimeOfDay.fromDateTime(_appointment!), alwaysUse24HourFormat: true)}';

Sık Sorulan Sorular

showDatePicker Türkçe nasıl yapılır?

pubspec.yaml dosyasına flutter_localizations eklenir, MaterialApp'e GlobalMaterialLocalizations.delegate ile diğer delegeler ve supportedLocales içinde Locale('tr') verilir. Cihaz dili Türkçe ise takvim kendiliğinden Türkçe açılır.

Tarih ve saat tek bir DateTime'da nasıl birleştirilir?

Önce showDatePicker, sonra showTimePicker çağrılır ve sonuçlar DateTime(date.year, date.month, date.day, time.hour, time.minute) ile birleştirilir. İki await arasında context.mounted kontrolü yapılmalıdır.

Belirli günler nasıl seçilemez yapılır?

showDatePicker'ın selectableDayPredicate parametresine, her gün için true ya da false döndüren bir fonksiyon verilir. initialDate olarak verilen günün de bu fonksiyondan true alması gerekir.

Saat seçici 24 saat formatında nasıl açılır?

showTimePicker'ın builder parametresinde child, alwaysUse24HourFormat: true verilmiş bir MediaQuery ile sarılır. Bu yapılmazsa format cihazın ayarlarına göre belirlenir.

Yorumlar