Flutter Tutorial

Flutter Beginner Lesson 32 of 135 3 min read

Dropdowns, Date Pickers and Time Pickers in Flutter

Let users choose from a list or pick a date and time in Flutter with DropdownMenu, showDatePicker, showTimePicker and date ranges.

On this page

When there are too many options to show at once, or the value is a date, use a menu or a picker.

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: CityPage()));

class CityPage extends StatefulWidget {
  const CityPage({super.key});

  @override
  State<CityPage> createState() => _CityPageState();
}

class _CityPageState extends State<CityPage> {
  static const _cities = ['Kathmandu', 'Pokhara', 'Lalitpur', 'Biratnagar', 'Butwal'];
  String? _city;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            DropdownMenu<String>(
              label: const Text('City'),
              width: 260,
              enableFilter: true, // type to narrow the list
              dropdownMenuEntries: [
                for (final c in _cities) DropdownMenuEntry(value: c, label: c),
              ],
              onSelected: (value) => setState(() => _city = value),
            ),
            const SizedBox(height: 16),
            Text(_city == null ? 'No city chosen' : 'You chose $_city'),
          ],
        ),
      ),
    );
  }
}

DropdownMenu is the Material 3 control. It supports typing to filter, which matters for long lists. Set initialSelection to preselect a value.

The older DropdownButton and DropdownButtonFormField are still common, and the form version takes part in Form validation.

Date picker

showDatePicker opens a dialog and returns the chosen date, or null if the user cancels.

DateTime? _birthday;

Future<void> _pickDate() async {
  final now = DateTime.now();
  final picked = await showDatePicker(
    context: context,
    initialDate: _birthday ?? DateTime(now.year - 20),
    firstDate: DateTime(1920),
    lastDate: now,
  );
  if (picked == null) return; // cancelled
  setState(() => _birthday = picked);
}

firstDate and lastDate are required and set the range that can be chosen. Use them to enforce your rules, such as no birthdays in the future, or no bookings in the past.

Time picker

TimeOfDay? _time;

Future<void> _pickTime() async {
  final picked = await showTimePicker(
    context: context,
    initialTime: _time ?? TimeOfDay.now(),
  );
  if (picked == null) return;
  setState(() => _time = picked);
}

// Display it in the user's 12- or 24-hour format:
Text(_time?.format(context) ?? 'Choose a time')

Date and time together

Future<DateTime?> pickDateTime(BuildContext context) async {
  final date = await showDatePicker(
    context: context,
    firstDate: DateTime.now(),
    lastDate: DateTime.now().add(const Duration(days: 365)),
  );
  if (date == null || !context.mounted) return null;

  final time = await showTimePicker(context: context, initialTime: TimeOfDay.now());
  if (time == null) return null;

  return DateTime(date.year, date.month, date.day, time.hour, time.minute);
}

Date range

final range = await showDateRangePicker(
  context: context,
  firstDate: DateTime.now(),
  lastDate: DateTime.now().add(const Duration(days: 365)),
);
if (range != null) {
  debugPrint('${range.start} to ${range.end}, ${range.duration.inDays} nights');
}

A field that opens a picker

Users expect a date to look like a form field. Use a read-only text field that opens the picker when tapped.

final _dateController = TextEditingController();

TextFormField(
  controller: _dateController,
  readOnly: true,
  decoration: const InputDecoration(
    labelText: 'Date of birth',
    suffixIcon: Icon(Icons.calendar_today),
    border: OutlineInputBorder(),
  ),
  validator: (v) => (v == null || v.isEmpty) ? 'Choose a date' : null,
  onTap: () async {
    final picked = await showDatePicker(
      context: context,
      firstDate: DateTime(1920),
      lastDate: DateTime.now(),
    );
    if (picked != null) {
      _dateController.text = '${picked.day}/${picked.month}/${picked.year}';
    }
  },
)

Formatting dates for display

DateTime.toString() is not user friendly. Use the intl package.

import 'package:intl/intl.dart';

DateFormat.yMMMd().format(date)          // Oct 10, 2026
DateFormat('EEEE, d MMMM').format(date)  // Saturday, 10 October
DateFormat.jm().format(date)             // 6:45 PM

Other ways to choose

NeedWidget
A few options in a menu from a buttonPopupMenuButton, MenuAnchor
Search with suggestionsSearchAnchor, Autocomplete
iOS-style wheelCupertinoPicker, CupertinoDatePicker
Pick from a long list with searchA full-screen page with a ListView and a search field

Try it yourself

Build an appointment booking form: a dropdown for the service, a date field limited to the next 30 days, a time field, and a summary line such as “Haircut on Saturday, 10 October at 3:30 PM”.

Practise in the playground Updated