Skip to content
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
9 changes: 6 additions & 3 deletions lib/calendar_day_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
/// in the app that need better day view
library calendar_day_view;

export 'package:linked_scroll_controller/linked_scroll_controller.dart'
show LinkedScrollControllerGroup;

export 'src/day_views/calendar_day_view_base.dart';
export 'src/day_views/category/category_overflow_calendar_day_view.dart';
export 'src/day_views/event_calendar_day_view.dart';
export 'src/day_views/in_row_calendar_day_view.dart';
export 'src/day_views/overflow/overflow_calendar_day_view.dart';
export 'src/day_views/category/category_overflow_calendar_day_view.dart';
export 'src/models/day_event.dart';
export 'src/models/categorized_day_event.dart';
export 'src/models/day_event.dart';
export 'src/models/event_category.dart';
export 'src/models/typedef.dart';
export 'src/day_views/calendar_day_view_base.dart';
101 changes: 98 additions & 3 deletions lib/src/day_views/category/category_overflow_calendar_day_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'package:calendar_day_view/src/extensions/list_extensions.dart';
import 'package:calendar_day_view/src/extensions/time_of_day_extension.dart';
import 'package:calendar_day_view/src/widgets/timed_rebuilder.dart';
import 'package:flutter/material.dart';
import 'package:linked_scroll_controller/linked_scroll_controller.dart';
import 'package:timezone/timezone.dart';

import '../../../calendar_day_view.dart';
Expand All @@ -21,13 +20,49 @@ typedef TitleRowBuilder = Widget Function({
Widget? logo,
});

typedef CategoryDayViewLayoutMetricsCallback = void Function(
CategoryDayViewLayoutMetrics metrics,
);

@immutable
class CategoryDayViewLayoutMetrics {
final Size bodyContentSize;
final Rect bodyViewport;

bool get isHorizontallyOverflowing =>
bodyContentSize.width > bodyViewport.width;

bool get isVerticallyOverflowing =>
bodyContentSize.height > bodyViewport.height;

@override
int get hashCode => Object.hash(bodyContentSize, bodyViewport);

const CategoryDayViewLayoutMetrics({
required this.bodyContentSize,
required this.bodyViewport,
});

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is CategoryDayViewLayoutMetrics &&
other.bodyContentSize == bodyContentSize &&
other.bodyViewport == bodyViewport;

@override
String toString() => 'CategoryDayViewLayoutMetrics('
'bodyContentSize: $bodyContentSize, bodyViewport: $bodyViewport)';
}

abstract class GroupingStrategy<T> {
({List<EventGroup<T>> grouped, List<CategorizedDayEvent<T>> nonGrouped})
groupEvents(List<CategorizedDayEvent<T>> events);
}

class NoGroupingStrategy<T> implements GroupingStrategy<T> {
const NoGroupingStrategy();

@override
({List<EventGroup<T>> grouped, List<CategorizedDayEvent<T>> nonGrouped})
groupEvents(List<CategorizedDayEvent<T>> events) {
Expand All @@ -49,6 +84,7 @@ class EventGroup<T> extends CategorizedDayEvent<T> {

abstract class GroupLayoutStrategy<T, U> {
const GroupLayoutStrategy();

bool canLayout(EventGroup<T> group) => true;

Widget layout(
Expand Down Expand Up @@ -93,13 +129,19 @@ class CategoryOverflowCalendarDayView<T, U> extends StatefulWidget
required this.minColumnWidth,
required this.timeLabelsFormatter,
required this.currentTimeFormatter,
this.horizontalScrollControllerGroup,
this.verticalScrollControllerGroup,
this.onLayoutMetrics,
ValueGetter<DateTime>? clock,
}) : clock = clock ?? DateTime.now,
super(key: key);

final Border? tableBodyBorder;
final Border? timeColumnBorder;
final double minColumnWidth;
final LinkedScrollControllerGroup? horizontalScrollControllerGroup;
final LinkedScrollControllerGroup? verticalScrollControllerGroup;
final CategoryDayViewLayoutMetricsCallback? onLayoutMetrics;
final ValueGetter<DateTime> clock;
final GroupingStrategy<T>? groupingStrategy;
final GroupLayoutStrategy<T, U>? groupLayoutStrategy;
Expand Down Expand Up @@ -176,13 +218,34 @@ class CategoryOverflowCalendarDayView<T, U> extends StatefulWidget

class _CategoryOverflowCalendarDayViewState<T, U>
extends State<CategoryOverflowCalendarDayView<T, U>> {
final _horizontalScrollLink = LinkedScrollControllerGroup();
late final _horizontalScrollLink =
widget.horizontalScrollControllerGroup ?? LinkedScrollControllerGroup();
late final _headerScrollController = _horizontalScrollLink.addAndGet();
late final _horizScrollController = _horizontalScrollLink.addAndGet();
final _verticalScrollLink = LinkedScrollControllerGroup();
late final _verticalScrollLink =
widget.verticalScrollControllerGroup ?? LinkedScrollControllerGroup();
late final _timeScrollController = _verticalScrollLink.addAndGet();
late final _vertScrollController = _verticalScrollLink.addAndGet();

/// Identifies the scrollable body so its viewport rect can be measured.
final _bodyKey = GlobalKey();

/// Measures the scrollable body's viewport relative to the day view's
/// top-left, so the result accounts for the header and any safe-area inset.
/// Returns null while the render tree is not ready to be measured.
Rect? _measureBodyViewport() {
final root = context.findRenderObject();
final body = _bodyKey.currentContext?.findRenderObject();
if (root is! RenderBox ||
body is! RenderBox ||
!root.hasSize ||
!body.hasSize) {
return null;
}

return body.localToGlobal(Offset.zero, ancestor: root) & body.size;
}

@override
Widget build(BuildContext context) {
final timeStart = widget.currentDate.copyTimeAndMinClean(widget.startOfDay);
Expand All @@ -207,6 +270,28 @@ class _CategoryOverflowCalendarDayViewState<T, U>
final rowLength = totalWidth - widget.timeColumnWidth;
final tileWidth = rowLength / categoriesCount;

final onLayoutMetrics = widget.onLayoutMetrics;

if (onLayoutMetrics != null) {
final bodyContentSize = Size(
rowLength,
rowHeight * timeList.length,
);

WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;

final bodyViewport = _measureBodyViewport();

if (bodyViewport == null) return;

onLayoutMetrics(CategoryDayViewLayoutMetrics(
bodyContentSize: bodyContentSize,
bodyViewport: bodyViewport,
));
});
}

return Column(
mainAxisSize: MainAxisSize.min,
children: [
Expand Down Expand Up @@ -261,6 +346,7 @@ class _CategoryOverflowCalendarDayViewState<T, U>
),
Expanded(
child: ClipPath(
key: _bodyKey,
clipper: VerticalClipper(),
child: DecoratedBox(
decoration: BoxDecoration(
Expand Down Expand Up @@ -314,6 +400,15 @@ class _CategoryOverflowCalendarDayViewState<T, U>
),
);
}

@override
void dispose() {
_headerScrollController.dispose();
_horizScrollController.dispose();
_timeScrollController.dispose();
_vertScrollController.dispose();
super.dispose();
}
}

class VerticalClipper extends CustomClipper<Path> {
Expand Down