Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tap on Backgrounds for DateEvents and AllDayEvents #20

Merged
merged 14 commits into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ class _TimetableExampleState extends State<TimetableExample> {
),
body: Timetable<BasicEvent>(
controller: _controller,
onEventBackgroundTap: (start, isAllDay) {
_showSnackBar('Background tapped $start is all day event $isAllDay');
},
eventBuilder: (event) {
return BasicEventWidget(
event,
Expand Down
1 change: 0 additions & 1 deletion lib/src/content/date_events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ class _DayEventsLayoutDelegate<E extends Event>

for (final event in events) {
final position = positions.eventPositions[event];

final top = timeToY(event.start)
.coerceAtMost(size.height - periodToY(minEventDuration))
.coerceAtMost(size.height - minEventHeight);
Expand Down
35 changes: 30 additions & 5 deletions lib/src/content/multi_date_content.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:black_hole_flutter/black_hole_flutter.dart';
import 'package:flutter/widgets.dart';
import 'package:time_machine/time_machine.dart';

import '../controller.dart';
import '../date_page_view.dart';
Expand All @@ -16,12 +17,14 @@ class MultiDateContent<E extends Event> extends StatefulWidget {
Key key,
@required this.controller,
@required this.eventBuilder,
this.onEventBackgroundTap,
}) : assert(controller != null),
assert(eventBuilder != null),
super(key: key);

final TimetableController<E> controller;
final EventBuilder<E> eventBuilder;
final OnEventBackgroundTapCallback onEventBackgroundTap;

@override
_MultiDateContentState<E> createState() => _MultiDateContentState<E>();
Expand All @@ -42,7 +45,6 @@ class _MultiDateContentState<E extends Event>
Widget build(BuildContext context) {
final theme = context.theme;
final timetableTheme = context.timetableTheme;

return CustomPaint(
painter: MultiDateBackgroundPainter(
controller: widget.controller,
Expand All @@ -56,13 +58,36 @@ class _MultiDateContentState<E extends Event>
child: DatePageView(
controller: widget.controller,
builder: (_, date) {
return StreamedDateEvents<E>(
date: date,
controller: widget.controller,
eventBuilder: widget.eventBuilder,
return LayoutBuilder(
builder: (context, constraints) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTapUp: widget.onEventBackgroundTap != null
? (details) {
_callOnEventBackgroundTap(details, date, constraints);
}
: null,
child: StreamedDateEvents<E>(
date: date,
controller: widget.controller,
eventBuilder: widget.eventBuilder,
),
);
},
);
},
),
);
}

void _callOnEventBackgroundTap(TapUpDetails details, LocalDate date, BoxConstraints constraints) {
final millis = details.localPosition.dy /
constraints.maxHeight *
TimeConstants.millisecondsPerDay;
final startTime = LocalTime.sinceMidnight(
Time(milliseconds: millis.floor()))
.atDate(date);
widget.onEventBackgroundTap(startTime, false);

}
}
3 changes: 3 additions & 0 deletions lib/src/content/timetable_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ class TimetableContent<E extends Event> extends StatelessWidget {
Key key,
@required this.controller,
@required this.eventBuilder,
this.onEventBackgroundTap,
}) : assert(controller != null),
assert(eventBuilder != null),
super(key: key);

final TimetableController<E> controller;
final EventBuilder<E> eventBuilder;
final OnEventBackgroundTapCallback onEventBackgroundTap;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -53,6 +55,7 @@ class TimetableContent<E extends Event> extends StatelessWidget {
child: MultiDateContent<E>(
controller: controller,
eventBuilder: eventBuilder,
onEventBackgroundTap: onEventBackgroundTap
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
onEventBackgroundTap: onEventBackgroundTap
onEventBackgroundTap: onEventBackgroundTap,

),
),
],
Expand Down
66 changes: 50 additions & 16 deletions lib/src/header/all_day_events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,48 @@ class AllDayEvents<E extends Event> extends StatelessWidget {
Key key,
@required this.controller,
@required this.allDayEventBuilder,
this.onEventBackgroundTap,
}) : assert(controller != null),
assert(allDayEventBuilder != null),
super(key: key);

final TimetableController<E> controller;
final AllDayEventBuilder<E> allDayEventBuilder;
final OnEventBackgroundTapCallback onEventBackgroundTap;

@override
Widget build(BuildContext context) {
return ClipRect(
child: ValueListenableBuilder<DateInterval>(
valueListenable: controller.currentlyVisibleDatesListenable,
builder: (_, visibleDates, __) {
return StreamBuilder<Iterable<E>>(
stream: controller.eventProvider
.getAllDayEventsIntersecting(visibleDates),
builder: (_, snapshot) {
var events = snapshot.data ?? [];
// The StreamBuilder gets recycled and initially still has a list of
// old events.
events = events.where((e) => e.intersectsInterval(visibleDates));

return ValueListenableBuilder(
valueListenable: controller.scrollControllers.pageListenable,
builder: (context, page, __) =>
_buildEventLayout(context, events, page),
child: LayoutBuilder(
builder: (context, constraints) {
return ValueListenableBuilder<DateInterval>(
valueListenable: controller.currentlyVisibleDatesListenable,
builder: (_, visibleDates, __) {
return StreamBuilder<Iterable<E>>(
stream: controller.eventProvider
.getAllDayEventsIntersecting(visibleDates),
builder: (_, snapshot) {
var events = snapshot.data ?? [];
// The StreamBuilder gets recycled and initially still has a list of
// old events.
events =
events.where((e) => e.intersectsInterval(visibleDates));

return ValueListenableBuilder(
valueListenable:
controller.scrollControllers.pageListenable,
builder: (context, page, __) => GestureDetector(
behavior: HitTestBehavior.translucent,
onTapUp: onEventBackgroundTap != null
? (details) {
_callOnAllDayEventBackgroundTap(
details, page, constraints);
}
: null,
child: _buildEventLayout(context, events, page),
),
);
},
);
},
);
Expand All @@ -53,6 +69,14 @@ class AllDayEvents<E extends Event> extends StatelessWidget {
);
}

void _callOnAllDayEventBackgroundTap(TapUpDetails details, double page, BoxConstraints constraints) {
final tappedCell = details.localPosition.dx /
(constraints.maxWidth / controller.visibleRange.visibleDays);
final date = LocalDate.fromEpochDay((page + tappedCell).floor());
final startTime = date.atMidnight();
onEventBackgroundTap(startTime, true);
}

Widget _buildEventLayout(
BuildContext context,
Iterable<E> events,
Expand Down Expand Up @@ -183,7 +207,9 @@ class _EventsLayout<E extends Event> extends RenderBox
_eventHeight = eventHeight;

VisibleRange _visibleRange;

VisibleRange get visibleRange => _visibleRange;

set visibleRange(VisibleRange value) {
assert(value != null);
if (_visibleRange == value) {
Expand All @@ -195,7 +221,9 @@ class _EventsLayout<E extends Event> extends RenderBox
}

DateInterval _currentlyVisibleDates;

DateInterval get currentlyVisibleDates => _currentlyVisibleDates;

set currentlyVisibleDates(DateInterval value) {
assert(value != null);
if (_currentlyVisibleDates == value) {
Expand All @@ -207,7 +235,9 @@ class _EventsLayout<E extends Event> extends RenderBox
}

double _page;

double get page => _page;

set page(double value) {
assert(value != null);
if (_page == value) {
Expand All @@ -219,7 +249,9 @@ class _EventsLayout<E extends Event> extends RenderBox
}

double _eventHeight;

double get eventHeight => _eventHeight;

set eventHeight(double value) {
assert(value != null);
if (_eventHeight == value) {
Expand Down Expand Up @@ -286,6 +318,7 @@ class _EventsLayout<E extends Event> extends RenderBox
@override
double computeMinIntrinsicHeight(double width) =>
_parallelEventCount() * eventHeight;

@override
double computeMaxIntrinsicHeight(double width) =>
_parallelEventCount() * eventHeight;
Expand Down Expand Up @@ -343,6 +376,7 @@ class _EventsLayout<E extends Event> extends RenderBox
}

bool _hasOverflow = false;

void _setSize() {
final parallelEvents = _parallelEventCount();
size = Size(constraints.maxWidth, parallelEvents * eventHeight);
Expand Down
3 changes: 3 additions & 0 deletions lib/src/header/timetable_header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ class TimetableHeader<E extends Event> extends StatelessWidget {
Key key,
@required this.controller,
@required this.allDayEventBuilder,
this.onEventBackgroundTap,
}) : assert(controller != null),
assert(allDayEventBuilder != null),
super(key: key);

final TimetableController<E> controller;
final AllDayEventBuilder<E> allDayEventBuilder;
final OnEventBackgroundTapCallback onEventBackgroundTap;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -49,6 +51,7 @@ class TimetableHeader<E extends Event> extends StatelessWidget {
),
AllDayEvents<E>(
controller: controller,
onEventBackgroundTap: onEventBackgroundTap,
allDayEventBuilder: allDayEventBuilder,
),
],
Expand Down
8 changes: 7 additions & 1 deletion lib/src/timetable.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:time_machine/time_machine.dart';

import 'all_day.dart';
import 'content/timetable_content.dart';
Expand All @@ -9,6 +10,7 @@ import 'header/timetable_header.dart';
import 'theme.dart';

typedef EventBuilder<E extends Event> = Widget Function(E event);
typedef OnEventBackgroundTapCallback = void Function(LocalDateTime start, bool isAllDay);
typedef AllDayEventBuilder<E extends Event> = Widget Function(
BuildContext context,
E event,
Expand All @@ -18,11 +20,13 @@ typedef AllDayEventBuilder<E extends Event> = Widget Function(
const double hourColumnWidth = 48;

class Timetable<E extends Event> extends StatelessWidget {

const Timetable({
Key key,
@required this.controller,
@required this.eventBuilder,
this.allDayEventBuilder,
this.onEventBackgroundTap,
this.theme,
}) : assert(controller != null),
assert(eventBuilder != null),
Expand All @@ -35,22 +39,24 @@ class Timetable<E extends Event> extends StatelessWidget {
///
/// If not set, [eventBuilder] will be used instead.
final AllDayEventBuilder<E> allDayEventBuilder;

final TimetableThemeData theme;
final OnEventBackgroundTapCallback onEventBackgroundTap;

@override
Widget build(BuildContext context) {
Widget child = Column(
children: <Widget>[
TimetableHeader<E>(
controller: controller,
onEventBackgroundTap: onEventBackgroundTap,
allDayEventBuilder:
allDayEventBuilder ?? (_, event, __) => eventBuilder(event),
),
Expanded(
child: TimetableContent<E>(
controller: controller,
eventBuilder: eventBuilder,
onEventBackgroundTap: onEventBackgroundTap
),
),
],
Expand Down