feat: added documentation and tests

This commit is contained in:
Freek van de Ven 2022-09-02 16:21:52 +02:00
parent c775e06f77
commit 0073d90212
10 changed files with 165 additions and 50 deletions

2
.gitignore vendored
View file

@ -28,3 +28,5 @@ migrate_working_dir/
.dart_tool/ .dart_tool/
.packages .packages
build/ build/
coverage/

View file

@ -1,3 +1,3 @@
## 0.0.1 ## [0.0.1] - 2 September 2022
* TODO: Describe initial release. * Initial release.

View file

@ -1,24 +1,20 @@
[![pub package](https://img.shields.io/pub/v/[PACKAGE NAME ON PUB].svg)](https://github.com/Iconica-Development) [![Build status](https://github.com/Iconica-Development/agenda)](URL TO GITHUB ACTIONS) [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart) [![pub package](https://img.shields.io/pub/v/[PACKAGE NAME ON PUB].svg)](https://github.com/Iconica-Development) [![Build status](https://github.com/Iconica-Development/agenda)](URL TO GITHUB ACTIONS) [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart)
Short description of what your package is, why you created it. What issues it fixes and how it works. Also mention the available platforms # Agenda
A Flutter package for creating an agenda that displays events per day with an included calendar for picking the date. Multiple events at the same time are alongside each other. There is also the option to stack multiple items at the same time.
The underlying datepicker widget supports marking dates and disabling dates.
## Setup
What setup steps are neccesarry and why>
<details> Supports all Flutter platforms.
<summary>PLATFORM</summary>
specific platform steps ## Usage
</details> To use this package, add `agenda` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels).
## How to use ### Example
How can we use the package descibe the most common ways with examples in See [Example Code](example/lib/main.dart) for more info.
```dart
codeblocks
```
## Issues ## Issues

View file

@ -57,6 +57,10 @@ class AgendaDemo extends StatelessWidget {
start: DateTime.now().subtract(const Duration(hours: 2)), start: DateTime.now().subtract(const Duration(hours: 2)),
end: DateTime.now().add(const Duration(hours: 1)), end: DateTime.now().add(const Duration(hours: 1)),
), ),
AgendaEvent(
start: DateTime.now().add(const Duration(days: 2)),
end: DateTime.now().add(const Duration(days: 3)),
),
], ],
disabledDates: [ disabledDates: [
// yesterday // yesterday

View file

@ -66,9 +66,9 @@ packages:
dependency: transitive dependency: transitive
description: description:
path: "." path: "."
ref: main ref: stable
resolved-ref: "7a3c66d28c2d29c1983d186435ed559a19fe34ff" resolved-ref: "6b3f1f5d12c1762bc208a1f1a4b9384dcb3369d4"
url: "https://github.com/Iconica-Development/flutter_date_time_picker" url: "git@github.com:Iconica-Development/flutter_date_time_picker.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
flutter_lints: flutter_lints:
@ -178,7 +178,7 @@ packages:
path: "." path: "."
ref: "v0.0.2" ref: "v0.0.2"
resolved-ref: "62fdaa443818bd646058b536a12304725d1619fd" resolved-ref: "62fdaa443818bd646058b536a12304725d1619fd"
url: "https://github.com/Iconica-Development/timetable" url: "git@github.com:Iconica-Development/timetable.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
vector_math: vector_math:

View file

@ -5,6 +5,9 @@ import 'package:flutter_date_time_picker/flutter_date_time_picker.dart';
import 'package:timetable/timetable.dart'; import 'package:timetable/timetable.dart';
class AgendaWidget extends StatefulWidget { 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({ const AgendaWidget({
required this.blocks, required this.blocks,
this.highlightedDates = const [], this.highlightedDates = const [],
@ -13,6 +16,7 @@ class AgendaWidget extends StatefulWidget {
this.header, this.header,
this.scrollController, this.scrollController,
this.scrollPhysics, this.scrollPhysics,
this.onTapDay,
this.startHour = 0, this.startHour = 0,
this.endHour = 24, this.endHour = 24,
this.hourHeight = 80, this.hourHeight = 80,
@ -26,15 +30,22 @@ class AgendaWidget extends StatefulWidget {
/// Header widget that is displayed above the datepicker. /// Header widget that is displayed above the datepicker.
final Widget? header; final Widget? header;
/// The blocks that are displayed in the agenda
final List<AgendaEvent> blocks; final List<AgendaEvent> blocks;
/// The highlighted dates that are displayed in the agenda
final List<DateTime> highlightedDates; final List<DateTime> highlightedDates;
/// The disabled dates that are displayed in the agenda
final List<DateTime> disabledDates; final List<DateTime> disabledDates;
/// The date that is initially selected. /// The date that is initially selected.
final DateTime? initialDate; 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; final bool highlightToday;
/// Hour at which the timetable starts. /// Hour at which the timetable starts.
@ -67,7 +78,7 @@ class AgendaWidget extends StatefulWidget {
} }
class _AgendaWidgetState extends State<AgendaWidget> { class _AgendaWidgetState extends State<AgendaWidget> {
DateTime? _selectedDate; late DateTime _selectedDate;
@override @override
void initState() { void initState() {
@ -77,9 +88,7 @@ class _AgendaWidgetState extends State<AgendaWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// filter out the blocks that are not on the selected date. var events = _filterEventsOnDay(widget.blocks, _selectedDate);
var events =
widget.blocks.where((block) => block.start.day == _selectedDate?.day);
return DateTimePicker( return DateTimePicker(
initialDate: _selectedDate, initialDate: _selectedDate,
pickTime: false, pickTime: false,
@ -101,16 +110,36 @@ class _AgendaWidgetState extends State<AgendaWidget> {
hourHeight: widget.hourHeight, hourHeight: widget.hourHeight,
startHour: widget.startHour, startHour: widget.startHour,
endHour: widget.endHour, endHour: widget.endHour,
timeBlocks: events.isEmpty timeBlocks: events,
? [] theme: widget.theme.tableTheme,
: events combineBlocks: true,
mergeBlocks: true,
),
);
}
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( .map(
(e) => TimeBlock( (e) => TimeBlock(
start: TimeOfDay( start: (e.start.day != day.day)
? TimeOfDay(hour: widget.startHour, minute: 0)
: TimeOfDay(
hour: e.start.hour, hour: e.start.hour,
minute: e.start.minute, minute: e.start.minute,
), ),
end: TimeOfDay( end: (e.end.day != day.day)
? TimeOfDay(hour: widget.endHour, minute: 0)
: TimeOfDay(
hour: e.end.hour, hour: e.end.hour,
minute: e.end.minute, minute: e.end.minute,
), ),
@ -118,11 +147,6 @@ class _AgendaWidgetState extends State<AgendaWidget> {
child: e.content, child: e.content,
), ),
) )
.toList(), .toList();
theme: widget.theme.tableTheme,
combineBlocks: true,
mergeBlocks: true,
),
);
} }
} }

View file

@ -1,12 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AgendaEvent { class AgendaEvent {
const AgendaEvent({ /// The model used for a single event in the [AgendaWidget].
/// AgendaEvent can be multiple days long.
AgendaEvent({
required this.start, required this.start,
required this.end, required this.end,
this.id, this.id,
this.content, this.content,
}); }) : assert(start.isBefore(end), 'start must be before end');
/// The start time of the event. /// The start time of the event.
final DateTime start; final DateTime start;

View file

@ -2,6 +2,10 @@ import 'package:flutter_date_time_picker/flutter_date_time_picker.dart';
import 'package:timetable/timetable.dart'; import 'package:timetable/timetable.dart';
class AgendaTheme { class AgendaTheme {
/// [AgendaTheme] is a class that contains all styling options
/// for the [AgendaWidget].
/// The underlying [TableTheme] is used for the timetable.
/// The [DateTimePickerTheme] is used for the datepicker.
const AgendaTheme({ const AgendaTheme({
this.tableTheme = const TableTheme(), this.tableTheme = const TableTheme(),
this.timePickerTheme = const DateTimePickerTheme(), this.timePickerTheme = const DateTimePickerTheme(),

View file

@ -14,11 +14,11 @@ dependencies:
sdk: flutter sdk: flutter
flutter_date_time_picker: flutter_date_time_picker:
git: git:
url: https://github.com/Iconica-Development/flutter_date_time_picker url: git@github.com:Iconica-Development/flutter_date_time_picker.git
ref: main ref: stable
timetable: timetable:
git: git:
url: https://github.com/Iconica-Development/timetable url: git@github.com:Iconica-Development/timetable.git
ref: v0.0.2 ref: v0.0.2
dev_dependencies: dev_dependencies:

View file

@ -1,7 +1,90 @@
import 'package:agenda/agenda.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
test('test', () { testWidgets('header is shown', (tester) async {
expect(true, true); // Act
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
body: AgendaWidget(
header: Text('Agenda'),
blocks: [],
),
),
),
);
// Assert
expect(find.text('Agenda'), findsOneWidget);
});
testWidgets('blocks are shown', (tester) async {
// Act
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: AgendaWidget(
blocks: [
AgendaEvent(
start: DateTime.now().subtract(const Duration(days: 3)),
end: DateTime.now().subtract(const Duration(days: 2)),
content: const Text('not shown'),
),
AgendaEvent(
start: DateTime.now().add(const Duration(hours: 3)),
end: DateTime.now().add(const Duration(hours: 4)),
id: 4,
content: const Text('event 4'),
),
AgendaEvent(
start: DateTime.now().add(const Duration(hours: 3)),
end: DateTime.now().add(const Duration(hours: 4)),
id: 4,
content: const Text('event 5'),
),
],
),
),
),
);
// Assert
expect(find.text('event 4'), findsOneWidget);
expect(find.text('event 5'), findsOneWidget);
expect(find.text('not shown'), findsNothing);
});
testWidgets('event is removed on tap', (tester) async {
// Act
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: AgendaWidget(
initialDate: DateTime.now(),
blocks: [
AgendaEvent(
start: DateTime.now(),
end: DateTime.now().add(const Duration(days: 1)),
content: const Text('single day event'),
),
],
),
),
),
);
var firstDay = find.text('single day event');
expect(firstDay, findsOneWidget);
var nextDay = DateTime.now().add(const Duration(days: 2));
// if nextDay is monday or tuesday we need to go back 4 days
if (nextDay.weekday == DateTime.tuesday ||
nextDay.weekday == DateTime.monday) {
nextDay = nextDay.subtract(const Duration(days: 4));
}
await tester.tap(find.text(nextDay.day.toString()));
await tester.pump();
expect(find.text('single day event'), findsNothing);
}); });
} }