Form

Time Field

A time field allows a time to be selected from a picker or input field.

It is recommended to use FTimeField.picker on touch devices and FTimeField.new on non-primarily touch devices.

The input field supports both arrow key navigation:

  • Up/Down arrows: Increment/decrement values
  • Left/Right arrows: Move between time segments

The input field does not support the following locales that use non-western numerals & scripts that require composing, it will default to English:

  • Arabic
  • Assamese
  • Bengali
  • Persian/Farsi
  • Marathi
  • Burmese
  • Nepali
  • Pashto
  • Tamil
  • Amharic
  • Kannada
  • Korean
  • Punjabi
  • Thai

The following locales will default to Chinese (zh):

  • Chinese (Hong Kong)
  • Chinese (Taiwan)
FTimeField(
  hour24: false,
  label: const Text('Appointment Time'),
  description: const Text('Select a Time for your appointment.'),
);

CLI

To generate and customize this style:

dart run forui style create time-field

Usage

FTimeField(...)

FTimeField(
  control: FTimeFieldControl.managed(
    initial: FTime(12, 30),
    validator: (time) => time != null && time < FTime(14, 30) ? 'Time must be in the future' : null,
    onChange: (time) {},
  ),
  style: (style) => style.copyWith(...),
  hour24: false,
  textAlign: TextAlign.start,
  textAlignVertical: TextAlignVertical.center,
  autofocus: false,
  expands: false,
  onEditingComplete: () {},
  onSubmit: (time) {},
  mouseCursor: SystemMouseCursors.text,
  canRequestFocus: true,
  builder: (context, styles, child) => child,
  prefixBuilder: (context, styles, _) => Icon(FIcons.clock4),
  suffixBuilder: null,
  label: const Text('Select Time'),
  description: const Text('Choose a time'),
  enabled: true,
  onSaved: (time) {},
  onReset: () {},
  autovalidateMode: AutovalidateMode.onUnfocus,
);

FTimeField.picker(...)

FTimeField.picker(
  control: FTimeFieldControl.managed(
    initial: FTime(12, 30),
    validator: (time) => time != null && time < FTime(14, 30) ? 'Time must be in the future' : null,
    onChange: (time) {},
  ),
  style: (style) => style.copyWith(...),
  hour24: false,
  hint: 'Select a time',
  hourInterval: 1,
  minuteInterval: 1,
  builder: (context, styles, child) => child,
  prefixBuilder: (context, styles, _) => Icon(FIcons.clock4),
  suffixBuilder: null,
  label: const Text('Picker Time'),
  description: const Text('Select a time'),
  enabled: true,
  onSaved: (time) {},
  onReset: () {},
  autovalidateMode: AutovalidateMode.onUnfocus,
);

Examples

24 Hours

FTimeField(
  hour24: true,
  label: const Text('Appointment Time'),
  description: const Text('Select a Time for your appointment.'),
);

Picker Only

FTimeField.picker(
  label: const Text('Appointment Time'),
  description: const Text('Select a time for your appointment.'),
);

Custom Validation

FTimeField(
  control: FTimeFieldControl.managed(validator: (time) => time?.hour == 12 ? 'Time cannot be noon.' : null),
  label: const Text('Appointment Time'),
  description: const Text('Select a time for your appointment.'),
);

Form

class FormTimeFieldPage extends StatefulWidget {
  @override
  State<FormTimeFieldPage> createState() => _FormTimeFieldPageState();
}

class _FormTimeFieldPageState extends State<FormTimeFieldPage> {
  final _key = GlobalKey<FormState>();
  late final _startController = FTimeFieldController(
    validator: (time) => switch (time) {
      null => 'Please select a start time.',
      _ when time < FTime.now() => 'Start Time must be in the future.',
      _ => null,
    },
  );

  @override
  void dispose() {
    _startController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Form(
    key: _key,
    child: Column(
      children: [
        FTimeField(
          control: FTimeFieldControl.managed(controller: _startController),
          label: const Text('Start Time'),
          description: const Text('Select a start time.'),
          autovalidateMode: AutovalidateMode.disabled,
        ),
        const SizedBox(height: 20),
        FTimeField(
          control: FTimeFieldControl.managed(
            validator: (time) => switch (time) {
              null => 'Please select an end time.',
              _ when _startController.value != null && time < _startController.value! =>
                'End Time must be after start time.',
              _ => null,
            },
          ),
          label: const Text('End Time'),
          description: const Text('Select an end time.'),
          autovalidateMode: AutovalidateMode.disabled,
        ),
        const SizedBox(height: 25),
        FButton(
          child: const Text('Submit'),
          onPress: () {
            if (_key.currentState!.validate()) {
              // Form is valid, process the dates
            }
          },
        ),
      ],
    ),
  );
}

On this page