1

There is a simple date picker widget built into Flutter - called DateRangePickerDialog. I'd like to select a starting hour and a finishing hour of an event. Where can I find a ready widget for that?

enter image description here

enter image description here

1 Answers1

1

Use the following code which provides a simple TimeRangePickerDialog. You can customize opening hours. The result is available in

Availability.periodStartIndex & Availability.periodStopIndex

Availability availability = Availability(
    date: DateTime.now(), reservationFrom: 9, reservationTo: 21,
    available: [9, 10,11,12,13,14,15,16,17,19,20,21,22
]);

@override
Widget build(BuildContext context) {
  return TimeRangePickerDialog(availability, 4, 4, 40);
}

TimeRangePickerDialog

import 'package:flutter/material.dart';
import 'availability.dart';
import 'picker_single_hour.dart';

class TimeRangePickerDialog extends StatefulWidget {
  final Availability data;
  final double crossAxisSpacing;
  final int crossAxisCount;
  final double cellHeight;

  TimeRangePickerDialog(
    this.data,
    this.crossAxisSpacing,
    this.crossAxisCount,
    this.cellHeight);

  @override
  State<TimeRangePickerDialog> createState() => _TimeRangePickerDialogState(
    data, crossAxisSpacing, crossAxisCount, cellHeight
  );
}

class _TimeRangePickerDialogState extends State<TimeRangePickerDialog> {
  final Availability data;
  final double crossAxisSpacing;
  final int crossAxisCount;
  final double cellHeight;

  _TimeRangePickerDialogState(this.data, this.crossAxisSpacing, this.crossAxisCount, this.cellHeight);

  @override
  Widget build(BuildContext context) {
    final _screenWidth = MediaQuery.of(context).size.width;
    final _width = (_screenWidth - ((crossAxisCount - 1) * crossAxisSpacing)) /
        crossAxisCount;
    final _aspectRatio = _width / cellHeight;

    return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: crossAxisCount, childAspectRatio: _aspectRatio),
        itemCount: data.hours.length,
        shrinkWrap: true,
        physics: NeverScrollableScrollPhysics(),
        itemBuilder: (context, index) => GestureDetector(
          onTap: () {
            setState(() {
              handleSelection(data, index);
            });
          },
          child: HourPickerSingleHour(
              selectAppropriateItemState(data, index),
              data.hours[index].time,
              data.date.param,
              data.hours[index].time,
              data.hours[index].availability
          ),
        ));
  }

  void handleSelection(Availability data, int index) {
    if (data.periodStartIndex == null && data.periodEndIndex == null)
      data.periodStartIndex = index;
    else if (data.periodStartIndex != null && data.periodEndIndex == null) {
      data.periodEndIndex = index;

      // prevent selection of hours in wrong order
      if (data.periodStartIndex != null && data.periodEndIndex != null &&
          data.periodStartIndex! > data.periodEndIndex!) {
        data.periodStartIndex = null;
        data.periodEndIndex = null;
      }
    }
    else {
      data.periodStartIndex = null;
      data.periodEndIndex = null;
    }
  }

  HourPickerHourState selectAppropriateItemState(Availability data, int index) {
    if (data.periodStartIndex == index)
      return HourPickerHourState.START;
    else if (data.periodEndIndex == index)
      return HourPickerHourState.END;
    else if (data.periodStartIndex != null && data.periodEndIndex != null &&
        data.periodStartIndex! < data.periodEndIndex! &&
        index < data.periodEndIndex!  && index > data.periodStartIndex!)
      return HourPickerHourState.MIDDLE;
    else return HourPickerHourState.EMPTY;
  }
}

HourPickerSingleHour

enum HourPickerHourState {
  START,
  MIDDLE,
  END,
  EMPTY
}

class HourPickerSingleHour extends StatelessWidget {
  final double START_END_CIRCLE_RADIUS = 16;
  final double START_END_PERIOD_MARGIN = 12;

  final HourPickerHourState state;
  final String timeHeader;
  final String dateParam;
  final String dateTime;
  final bool hoursAvailability;

  const HourPickerSingleHour(
      this.state,
      this.timeHeader,
      this.dateParam,
      this.dateTime,
      this.hoursAvailability);

  @override
  Widget build(BuildContext context) {
    EdgeInsets margin = EdgeInsets.only(
        left: state == HourPickerHourState.START ? START_END_PERIOD_MARGIN : 0,
        right: state == HourPickerHourState.END ? START_END_PERIOD_MARGIN : 0,
        bottom: 0,
        top: 0,
    );
    Color backgroundColor = Color.fromARGB(
        state == HourPickerHourState.EMPTY ? 0 : 35, 0, 0, 255);

    return Container(
      margin: margin,
      decoration: BoxDecoration(
        color: backgroundColor,
        borderRadius: BorderRadius.only(
          topLeft: state == HourPickerHourState.START ? Radius.circular(START_END_CIRCLE_RADIUS) : Radius.circular(0),
          bottomLeft: state == HourPickerHourState.START ? Radius.circular(START_END_CIRCLE_RADIUS) : Radius.circular(0),
          topRight: state == HourPickerHourState.END ? Radius.circular(START_END_CIRCLE_RADIUS) : Radius.circular(0),
          bottomRight: state == HourPickerHourState.END ? Radius.circular(START_END_CIRCLE_RADIUS) : Radius.circular(0),
        )
      ),
      alignment: Alignment.center,
      child: Text(
        timeHeader,
        key: Key(
            'text_dayHour_${dateParam}_${dateTime}'),
        textAlign: TextAlign.center,
        style: TextStyle(
            fontFamily: 'SofiaPro',
            fontSize: 16,
            color: hoursAvailability
                ? Colors.grey
                : Colors.blueGrey),
      ),
    );
  }
}

Availability

import 'package:intl/intl.dart';
import 'hour_availability.dart';

class Availability {
  DateTime date;
  int reservationFrom;
  int reservationTo;
  List<int> available;

  int? periodStartIndex;
  int? periodEndIndex;

  Availability(
      {required this.date,
      required this.reservationFrom,
      required this.reservationTo,
      required this.available});


  List<HourAvailability> get hours {
    final now = DateTime.now();
    List<HourAvailability> list = [];
    for (var i = reservationFrom; i <= reservationTo; i++) {
      String time = DateTime(now.year, now.month, now.day, i, 0).time;
      list.add(HourAvailability(i, time, _specifyAvailability(i)));
    }
    return list;
  }

  bool _specifyAvailability(int i) =>
      available.contains(i) ||
          (i == reservationTo &&
              available.isNotEmpty &&
              available.last == (reservationTo - 1));
}

extension DateTimeFormat on DateTime {
  String get param => DateFormat('yyyy-MM-dd').format(this);
  String get time => DateFormat('hh:mm a').format(this);
}

HourAvailability

class HourAvailability {
  final int hour;
  final String time;
  final bool availability;

  HourAvailability(this.hour, this.time, this.availability);
}