flutter_agenda/lib/src/agenda.dart

230 lines
7.4 KiB
Dart

// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart';
import 'package:flutter_agenda/src/models/agenda_event.dart';
import 'package:flutter_agenda/src/models/agenda_theme.dart';
import 'package:flutter_date_time_picker/flutter_date_time_picker.dart';
import 'package:flutter_timetable/flutter_timetable.dart';
class AgendaWidget extends StatefulWidget {
/// [AgendaWidget] is a widget that displays a timetable with events.
/// It is stateful and sorts the events based on the selected date.
/// All styling can be configured through the [AgendaTheme] class.
const AgendaWidget({
required this.blocks,
this.tableDirection = Axis.vertical,
this.size,
this.highlightedDates = const [],
this.disabledDates = const [],
this.initialDate,
this.alwaysUse24HourFormat,
this.header,
this.childIfEmptyRoster,
this.scrollController,
this.scrollPhysics,
this.onTapDay,
this.tableTopPadding = 0,
this.datePickerExpansion = 0,
this.startHour = 0,
this.endHour = 24,
this.hourDimension = 80,
this.highlightToday = true,
this.updateEmptyChildPosition = true,
this.blockDimension = 50,
this.blockColor = const Color(0x80FF0000),
this.theme = const AgendaTheme(),
super.key,
});
/// The [Axis] along which the timetable markings are lined out
final Axis tableDirection;
/// Header widget that is displayed above the datepicker.
final Widget? header;
/// The Widget displayed instead of the timetable when no events are available
final Widget? childIfEmptyRoster;
/// Whether to change the position of the Empty Child Widget
/// when the datepicker is opened
final bool updateEmptyChildPosition;
/// The blocks that are displayed in the agenda
final List<AgendaEvent> blocks;
/// The highlighted dates that are displayed in the agenda
final List<DateTime> highlightedDates;
/// The disabled dates that are displayed in the agenda
final List<DateTime> disabledDates;
/// The date that is initially selected.
final DateTime? initialDate;
/// Function called when the user taps on a day in the datepicker.
final Function(DateTime)? onTapDay;
/// Whether to highlight the current date in the agenda.
final bool highlightToday;
/// Hour at which the timetable starts.
final int startHour;
/// Hour at which the timetable ends.
final int endHour;
/// The amount of pixels above the timetable
final double tableTopPadding;
/// The extra height of the datePicker when it is expanded
final double datePickerExpansion;
/// [bool] to set the clock on [TimePickerDialog] to a fixed 24 format.
/// By default this gets determined by the settings on the user device.
final bool? alwaysUse24HourFormat;
/// The dimension in pixels of one hour in the timetable.
final double hourDimension;
/// The dimension in pixels of the rosterItem if there is no child
final double blockDimension;
/// The color of the agendaItem if there is no child
final Color blockColor;
/// The theme used by the agenda.
/// The [TableTheme] used by the timetable is included.
final AgendaTheme theme;
/// The [Size] of the timetable.
final Size? size;
/// The scroll controller to control the scrolling of the timetable.
final ScrollController? scrollController;
/// The scroll physics used for the SinglechildScrollView.
final ScrollPhysics? scrollPhysics;
@override
State<AgendaWidget> createState() => _AgendaWidgetState();
}
class _AgendaWidgetState extends State<AgendaWidget> {
late DateTime _selectedDate;
double _scrollOffset = 0.0;
@override
void initState() {
super.initState();
_selectedDate = widget.initialDate ?? DateTime.now();
}
@override
Widget build(BuildContext context) {
var events = _filterEventsOnDay(widget.blocks, _selectedDate);
return DragDownDateTimePicker(
alwaysUse24HourFormat: widget.alwaysUse24HourFormat,
initialDate: _selectedDate,
pickTime: false,
highlightToday: widget.highlightToday,
header: widget.header,
onTapDay: (selected) {
widget.onTapDay?.call(selected);
setState(() {
_selectedDate = selected;
});
},
onTimerPickerSheetChange: (p0) {
if (widget.updateEmptyChildPosition) {
setState(() {
_scrollOffset = p0 - widget.tableTopPadding;
});
}
},
disabledDates: widget.disabledDates,
markedDates: widget.highlightedDates,
dateTimePickerTheme: widget.theme.timePickerTheme,
child: Column(
children: [
SizedBox(
height: widget.tableTopPadding,
),
SizedBox(
height: (widget.size != null)
? widget.size!.height - widget.tableTopPadding
: null,
width: (widget.size != null) ? widget.size!.width : null,
child: (widget.childIfEmptyRoster != null && events.isEmpty)
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// add empty space between the top of the widget
//and the child if the datepicker is expanded
SizedBox(
height: _scrollOffset,
),
widget.childIfEmptyRoster!,
],
)
: Timetable(
tableDirection: widget.tableDirection,
scrollPhysics: widget.scrollPhysics,
scrollController: widget.scrollController,
blockColor: widget.blockColor,
blockDimension: widget.blockDimension,
hourDimension: widget.hourDimension,
startHour: widget.startHour,
endHour: widget.endHour,
timeBlocks: events,
theme: widget.theme.tableTheme,
combineBlocks: true,
mergeBlocks: true,
size: (widget.size != null)
? Size(
widget.size!.width,
widget.size!.height - widget.tableTopPadding,
)
: null,
),
),
],
),
);
}
List<TimeBlock> _filterEventsOnDay(List<AgendaEvent> events, DateTime day) {
return events
.where(
(e) =>
(e.start.day == day.day &&
e.start.month == day.month &&
e.start.year == day.year) ||
(e.end.day == day.day &&
e.end.month == day.month &&
e.end.year == day.year),
)
.map(
(e) => TimeBlock(
start: (e.start.day != day.day)
? TimeOfDay(hour: widget.startHour, minute: 0)
: TimeOfDay(
hour: e.start.hour,
minute: e.start.minute,
),
end: (e.end.day != day.day)
? TimeOfDay(hour: widget.endHour, minute: 0)
: TimeOfDay(
hour: e.end.hour,
minute: e.end.minute,
),
id: e.id ?? 0,
childDimension: e.childDimension,
child: e.content,
),
)
.toList();
}
}