From 1bd7294ef453882fad281b43ceaae50a166fdca9 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Tue, 27 Dec 2022 10:19:05 +0100 Subject: [PATCH] feat: add horizontal version of agenda --- CHANGELOG.md | 6 ++ example/lib/main.dart | 4 +- example/pubspec.lock | 14 ++-- example/pubspec.yaml | 2 +- lib/agenda.dart | 11 --- lib/flutter_agenda.dart | 11 +++ lib/src/agenda.dart | 115 +++++++++++++++++++++++++------ lib/src/models/agenda_event.dart | 8 ++- pubspec.yaml | 8 +-- test/agenda_test.dart | 5 +- 10 files changed, 137 insertions(+), 47 deletions(-) delete mode 100644 lib/agenda.dart create mode 100644 lib/flutter_agenda.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 71d2704..95d5623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [1.0.0] - 27 December 2022 + +* Added option for custom Widget when there are no events for a given day +* Support for both horizontal and vertical variant +* Adjustable size for the component + ## [0.0.1] - 2 September 2022 * Initial release. diff --git a/example/lib/main.dart b/example/lib/main.dart index f06c952..64880e3 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:agenda/agenda.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_agenda/flutter_agenda.dart'; void main() { runApp( @@ -25,7 +25,7 @@ class AgendaDemo extends StatelessWidget { 'Agenda', style: Theme.of(context).textTheme.headline6, ), - blockWidth: 50, + blockDimension: 50, highlightToday: false, blocks: [ AgendaEvent( diff --git a/example/pubspec.lock b/example/pubspec.lock index 551695a..8f88bf3 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,13 +1,6 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - agenda: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.0.1" async: dependency: transitive description: @@ -62,6 +55,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_agenda: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.0" flutter_date_time_picker: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 659706d..55571d0 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.2 - agenda: + flutter_agenda: path: ../ dev_dependencies: diff --git a/lib/agenda.dart b/lib/agenda.dart deleted file mode 100644 index e28d7ca..0000000 --- a/lib/agenda.dart +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Iconica -// -// SPDX-License-Identifier: BSD-3-Clause - -library agenda; - -export 'package:agenda/src/agenda.dart'; -export 'package:agenda/src/models/agenda_event.dart'; -export 'package:agenda/src/models/agenda_theme.dart'; - -export 'package:timetable/timetable.dart'; diff --git a/lib/flutter_agenda.dart b/lib/flutter_agenda.dart new file mode 100644 index 0000000..0ff9740 --- /dev/null +++ b/lib/flutter_agenda.dart @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Iconica +// +// SPDX-License-Identifier: BSD-3-Clause + +library agenda; + +export 'package:flutter_agenda/src/agenda.dart'; +export 'package:flutter_agenda/src/models/agenda_event.dart'; +export 'package:flutter_agenda/src/models/agenda_theme.dart'; + +export 'package:timetable/timetable.dart'; diff --git a/lib/src/agenda.dart b/lib/src/agenda.dart index a103a04..d1cf9ab 100644 --- a/lib/src/agenda.dart +++ b/lib/src/agenda.dart @@ -2,9 +2,9 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:agenda/src/models/agenda_event.dart'; -import 'package:agenda/src/models/agenda_theme.dart'; 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:timetable/timetable.dart'; @@ -14,26 +14,43 @@ class AgendaWidget extends StatefulWidget { /// 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.hourHeight = 80, + this.hourDimension = 80, this.highlightToday = true, - this.blockWidth = 50, + 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 blocks; @@ -58,11 +75,21 @@ class AgendaWidget extends StatefulWidget { /// Hour at which the timetable ends. final int endHour; - /// The heigh of one hour in the timetable. - final double hourHeight; + /// The amount of pixels above the timetable + final double tableTopPadding; - /// The width of the agendaItem if there is no child - final double blockWidth; + /// 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; @@ -71,6 +98,9 @@ class AgendaWidget extends StatefulWidget { /// 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; @@ -83,6 +113,7 @@ class AgendaWidget extends StatefulWidget { class _AgendaWidgetState extends State { late DateTime _selectedDate; + double _scrollOffset = 0.0; @override void initState() { @@ -94,28 +125,71 @@ class _AgendaWidgetState extends State { 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: (p0) { + onTapDay: (selected) { + widget.onTapDay?.call(selected); setState(() { - _selectedDate = p0; + _selectedDate = selected; }); }, + onTimerPickerSheetChange: (p0) { + if (widget.updateEmptyChildPosition) { + setState(() { + _scrollOffset = p0 - widget.tableTopPadding; + }); + } + }, disabledDates: widget.disabledDates, markedDates: widget.highlightedDates, dateTimePickerTheme: widget.theme.timePickerTheme, - child: Timetable( - scrollPhysics: widget.scrollPhysics, - scrollController: widget.scrollController, - blockColor: widget.blockColor, - startHour: widget.startHour, - endHour: widget.endHour, - timeBlocks: events, - theme: widget.theme.tableTheme, - combineBlocks: true, - mergeBlocks: true, + 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, + ), + ), + ], ), ); } @@ -146,6 +220,7 @@ class _AgendaWidgetState extends State { minute: e.end.minute, ), id: e.id ?? 0, + childDimension: e.childDimension, child: e.content, ), ) diff --git a/lib/src/models/agenda_event.dart b/lib/src/models/agenda_event.dart index 8dba781..411f568 100644 --- a/lib/src/models/agenda_event.dart +++ b/lib/src/models/agenda_event.dart @@ -12,6 +12,7 @@ class AgendaEvent { required this.end, this.id, this.content, + this.childDimension, }) : assert(start.isBefore(end), 'start must be before end'); /// The start time of the event. @@ -20,9 +21,14 @@ class AgendaEvent { /// The end time of the event. final DateTime end; - /// + /// The [Widget] displayed inside the roster at the event time final Widget? content; + /// The dimension of the child in the axis of the timetable which it expands. + /// Only needed when child is not null and the horizontalvariant is used. + /// This is used to make the timetable background as large as all the blocks. + final double? childDimension; + /// The identifier of the event that is used to combine events /// with the same id. Leave empty or 0 if you don't want to combine events. final int? id; diff --git a/pubspec.yaml b/pubspec.yaml index 15533d2..f088a0d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ -name: agenda -description: Agenda widget with timetable -version: 0.0.1 -homepage: https://github.com/Iconica-Development/agenda +name: flutter_agenda +description: Agenda widget with an underlying timetable included +version: 1.0.0 +homepage: https://github.com/Iconica-Development/flutter_agenda publish_to: none diff --git a/test/agenda_test.dart b/test/agenda_test.dart index 15f6fc0..036ee4b 100644 --- a/test/agenda_test.dart +++ b/test/agenda_test.dart @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:agenda/agenda.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_agenda/flutter_agenda.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -13,6 +13,7 @@ void main() { const MaterialApp( home: Scaffold( body: AgendaWidget( + size: const Size(500, 500), header: Text('Agenda'), blocks: [], ), @@ -30,6 +31,7 @@ void main() { MaterialApp( home: Scaffold( body: AgendaWidget( + size: const Size(500, 500), blocks: [ AgendaEvent( start: DateTime.now().subtract(const Duration(days: 3)), @@ -66,6 +68,7 @@ void main() { MaterialApp( home: Scaffold( body: AgendaWidget( + size: const Size(500, 500), initialDate: DateTime.now(), blocks: [ AgendaEvent(