Flutter: Datums- und Zeitauswahl (DatePicker & TimePicker)
Zuletzt aktualisiert:
5 Min. Lesezeit

Geburtsdatum, Terminzeit oder Erinnerungszeitpunkt von Hand eintippen zu lassen ist mühsam und fehleranfällig: Der eine schreibt „3/5/2026“, die andere „05.03.26“. Die in Flutter eingebauten Funktionen showDatePicker und showTimePicker lösen dieses Problem. Sie öffnen einen Kalender- oder Uhr-Dialog im Material-Stil und liefern uns direkt ein DateTime- bzw. TimeOfDay-Objekt. Beide arbeiten asynchron, wir warten also mit await auf das Ergebnis.
Live-Demo
Sie können diese Widgets im interaktiven Beispiel unten ausprobieren:
💡 Falls das Beispiel oben nicht lädt, klicken Sie auf DartPad, um es in einem neuen Tab auszuführen.
DatePicker
Öffnet eine Kalenderansicht, in der Tag, Monat und Jahr ausgewählt werden.
Grundlegende Verwendung
Die Funktion liefert Future<DateTime?>. Tippt der Nutzer auf „Abbrechen“ oder neben den Dialog, ist das Ergebnis null:
DateTime? selectedDate;
Future<void> _selectDate() async {
final now = DateTime.now();
final DateTime? picked = await showDatePicker(
context: context,
initialDate: selectedDate ?? now, // Beim Öffnen markiertes Datum
firstDate: DateTime(2000), // Frühestes wählbares Datum
lastDate: DateTime(now.year + 5), // Spätestes wählbares Datum
);
if (picked != null && picked != selectedDate) {
setState(() {
selectedDate = picked;
});
}
}Beachten Sie, dass lastDate relativ zum heutigen Datum berechnet wird (now.year + 5) statt mit einer fest eingetragenen Jahreszahl; warum, erkläre ich im Abschnitt zu den Fehlern. Wer mit await noch nicht vertraut ist, liest am besten zuerst den Beitrag zu async/await.
Aufruf über einen Button:
ElevatedButton(
onPressed: _selectDate,
child: Text(
selectedDate == null
? 'Datum wählen'
: MaterialLocalizations.of(context).formatShortDate(selectedDate!),
),
)Statt das Datum per "${d.day}/${d.month}/${d.year}" von Hand zusammenzusetzen, liefert MaterialLocalizations ohne Zusatzpaket ein Format, das zur Sprache der App passt. Brauchen Sie freiere Formate, übernimmt DateFormat aus dem Paket intl.
Weit zurückliegende Daten wie ein Geburtsdatum
In der Standard-Kalenderansicht bedeutet ein Sprung um dreißig Jahre Dutzende Tipps. Zwei Parameter lösen das:
showDatePicker(
context: context,
initialDate: DateTime(2000),
firstDate: DateTime(1920),
lastDate: DateTime.now(),
initialDatePickerMode: DatePickerMode.year, // Zuerst die Jahresliste öffnen
initialEntryMode: DatePickerEntryMode.calendar, // Mit .input tippt der Nutzer das Datum ein
);TimePicker
Ermöglicht die Auswahl von Stunde und Minute.
Grundlegende Verwendung
Diese Funktion liefert Future<TimeOfDay?>.
TimeOfDay selectedTime = TimeOfDay.now();
Future<void> _selectTime() async {
final TimeOfDay? picked = await showTimePicker(
context: context,
initialTime: selectedTime,
builder: (context, child) {
return MediaQuery(
// 24-Stunden-Format erzwingen (optional)
data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
child: child!,
);
},
);
if (picked != null && picked != selectedTime) {
setState(() {
selectedTime = picked;
});
}
}TimeOfDay enthält nur Stunde und Minute, kein Datum. Zur Anzeige reicht selectedTime.format(context). Wie Datum und Uhrzeit zu einem einzigen DateTime zusammengeführt werden, zeigt das Szenario weiter unten.
Lokalisierung
Damit der Kalender auf Deutsch erscheint, fügen wir zuerst Flutters eigenes Lokalisierungspaket in pubspec.yaml hinzu:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutterAnschließend bekommt MaterialApp die Delegates und die unterstützten Sprachen:
import 'package:flutter_localizations/flutter_localizations.dart';
MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en'), // Englisch
Locale('de'), // Deutsch
],
// ...
)Danach richtet showDatePicker Tages- und Monatsnamen, Buttontexte und den ersten Wochentag selbstständig an der Gerätesprache aus (oder an der Sprache, die Sie über den Parameter locale angeben).
Theme anpassen
Um die Farben des Pickers zu ändern, umhüllen wir den Dialog über den Parameter builder mit einem Theme:
showDatePicker(
context: context,
// ... Parameter
builder: (context, child) {
final theme = Theme.of(context);
return Theme(
data: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(
primary: Colors.red, // Farbe des gewählten Tags und der Kopfzeile
onPrimary: Colors.white, // Textfarbe auf dem gewählten Tag
),
),
child: child!,
);
},
);Das bestehende colorScheme per copyWith anzupassen ist sicherer, als ein neues ColorScheme.light(...) zu bauen; so wird der Kalender im dunklen Theme nicht plötzlich weiß. Soll die ganze App dasselbe Aussehen bekommen, nutzen Sie die Felder datePickerTheme und timePickerTheme in ThemeData.
Wann verwenden – und wann nicht?
- Verwenden für einen einzelnen Tag oder eine einzelne Uhrzeit, wenn der Nutzer mit Blick auf den Kalender entscheidet (Termin, Liefertermin).
- Nicht verwenden für einen Zeitraum von–bis. Statt zwei Picker nacheinander zu öffnen, wählt
showDateRangePickerden Bereich in einem einzigen Dialog. - Nicht verwenden, wenn der Kalender Teil der Seite sein soll. Für einen eingebetteten Kalender setzen Sie das Widget
CalendarDatePickerdirekt ins Layout statt in einen Dialog. - Nicht verwenden, wenn ein iOS-typisches Aussehen wichtig ist. Dafür gibt es den
CupertinoDatePickerim Walzen-Stil, meist in einem Bottom Sheet. - Nicht verwenden für Monat/Jahr-Eingaben wie das Ablaufdatum einer Kreditkarte. Ein formatiertes Textfeld oder zwei kurze Dropdowns sind schneller als ein Kalender.
Soll das gewählte Datum in einem Formularfeld erscheinen, ist ein TextFormField mit readOnly: true, dessen onTap den Picker öffnet, ein verbreitetes und praktisches Muster; das Feld nimmt dann wie jedes andere an der Form-Validierung teil.
Häufige Fehler
1. initialDate liegt außerhalb des Bereichs
Symptom: Der Picker öffnet sich nicht; in der Konsole steht eine Assertion wie „initialDate … must be on or before lastDate …“ (oder „on or after firstDate“).
Ursache: meist eine fest eingetragene Jahreszahl. Code mit lastDate: DateTime(2025) läuft in diesem Jahr problemlos; sobald der Kalender auf 2026 springt, liegt initialDate: DateTime.now() außerhalb des Bereichs, und die App ist eines Morgens von selbst kaputt.
Lösung: Grenzen aus DateTime.now() ableiten und vor der Verwendung eines gespeicherten Datums als initialDate prüfen, ob es im Bereich liegt.
2. Context nach einem await verwenden
Symptom: Beim Abfragen von Datum und direkt danach Uhrzeit meldet der zweite Aufruf die Lint-Warnung use_build_context_synchronously („Don't use 'BuildContext's across async gaps“).
Ursache: Der Nutzer kann die Seite verlassen haben, während der Kalender offen war; wenn await zurückkehrt, ist der Context in unserer Hand womöglich nicht mehr gültig. Dasselbe gilt für den AlertDialog, denn Picker sind im Kern ebenfalls Dialoge.
Lösung: nach jedem await prüfen:
final date = await showDatePicker(/* ... */);
if (date == null || !context.mounted) return;
final time = await showTimePicker(context: context, initialTime: TimeOfDay.now());3. Der Kalender bleibt englisch
Symptom: Die App ist auf Deutsch, aber im Kalender steht „MON, TUE“ und „CANCEL“.
Lösung: Das Paket flutter_localizations hinzufügen und die oben gezeigten Delegates samt supportedLocales eintragen. Nur locale: const Locale('de') ohne Delegates zu übergeben bewirkt nichts.
4. Das Datum rutscht auf dem Server einen Tag zurück
Symptom: Der Nutzer wählt den 15. Mai, in der Datenbank steht der 14. Mai.
Ursache: showDatePicker liefert Mitternacht in lokaler Zeit. Schicken wir das mit picked.toUtc().toIso8601String(), ist lokale Mitternacht in jeder Zeitzone vor UTC (etwa Mitteleuropa) in UTC noch der Abend des Vortags.
Lösung: Zeitunabhängige Daten wie Geburtstage nur als Datum senden:
String toDateOnly(DateTime d) =>
'${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';Mini-Szenario: Terminwahl nur an Werktagen
Stellen Sie sich einen Terminbildschirm vor. Die Regeln: innerhalb der nächsten 30 Tage, nur an einem Werktag; danach wird die Uhrzeit abgefragt, und beides wird zu einem einzigen DateTime zusammengeführt.
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: 'Termintag',
);
if (date == null || !mounted) return;
final time = await showTimePicker(
context: context,
initialTime: const TimeOfDay(hour: 10, minute: 0),
helpText: 'Terminzeit',
);
if (time == null || !mounted) return;
setState(() {
_appointment = DateTime(
date.year, date.month, date.day, time.hour, time.minute,
);
});
}selectableDayPredicate graut Wochenenden im Kalender aus und macht sie unantippbar. Ein Detail übersieht man dabei leicht: Auch initialDate muss dieser Regel genügen. Ist heute Samstag und wir übergeben initialDate: today, bricht der Picker mit einer Assertion ab, statt sich zu öffnen. Genau dafür gibt es _nextWeekday; die Funktion verschiebt das Startdatum auf den ersten erlaubten Tag. DateUtils.addDaysToDate statt Duration(days: 1) verhindert außerdem, dass die Uhrzeit bei der Umstellung auf Sommer- oder Winterzeit verrutscht.
Bricht der Nutzer in einem der beiden Schritte ab, kehrt die Funktion vorzeitig zurück und nichts ändert sich; ein halbfertiger Termin kann so nicht entstehen. Für die Anzeige reicht wieder MaterialLocalizations:
final loc = MaterialLocalizations.of(context);
final label = _appointment == null
? 'Termin wählen'
: '${loc.formatFullDate(_appointment!)} · '
'${loc.formatTimeOfDay(TimeOfDay.fromDateTime(_appointment!), alwaysUse24HourFormat: true)}';Häufig gestellte Fragen
Wie zeige ich showDatePicker auf Deutsch an?
flutter_localizations in pubspec.yaml eintragen, GlobalMaterialLocalizations.delegate und die weiteren Delegates an MaterialApp übergeben und Locale('de') in supportedLocales aufnehmen. Ist die Gerätesprache Deutsch, öffnet sich der Kalender automatisch auf Deutsch.
Wie führe ich Datum und Uhrzeit zu einem DateTime zusammen?
Zuerst showDatePicker, dann showTimePicker aufrufen und die Ergebnisse mit DateTime(date.year, date.month, date.day, time.hour, time.minute) verbinden. Zwischen den beiden await-Aufrufen gehört eine Prüfung auf context.mounted.
Wie mache ich bestimmte Tage nicht auswählbar?
Dem Parameter selectableDayPredicate von showDatePicker eine Funktion übergeben, die für jeden Tag true oder false liefert. Der als initialDate übergebene Tag muss von dieser Funktion ebenfalls true bekommen.
Wie öffne ich den TimePicker im 24-Stunden-Format?
Im Parameter builder von showTimePicker den child mit einer MediaQuery umhüllen, deren Daten alwaysUse24HourFormat: true enthalten. Ohne diese Einstellung richtet sich das Format nach den Geräteeinstellungen.
Verwandte Artikel
Flutter: Helles und dunkles Theme mit ThemeData
Ein Flutter-Theme mit ThemeData aufbauen: ColorScheme.fromSeed, helles und dunkles Theme, ThemeMode, Theme.of, TextTheme, Komponenten-Themes, ThemeExtension.
Flutter: TextField und TextEditingController verwenden
TextField in Flutter: TextEditingController und dispose, InputDecoration, Tastaturtypen, inputFormatters, Passwortfelder, FocusNode und onChanged im Vergleich.
Flutter: Widgets überlagern mit Stack und Positioned
Stack und Positioned in Flutter: wie ein Stack seine Größe bestimmt, fit, alignment, clipBehavior, Positioned.fill, PositionedDirectional, Badge und Overlays.