Skip to content

Commit

Permalink
Merge pull request #13 from desmeit/master
Browse files Browse the repository at this point in the history
added an initial calendar date to allow for bi-directional pagination
  • Loading branch information
casvanluijtelaar authored Feb 26, 2022
2 parents 7161293 + 9aea3ce commit 8b6cf2a
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 60 deletions.
Empty file added analysis_options.yaml
Empty file.
14 changes: 7 additions & 7 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
Expand All @@ -21,7 +21,7 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
charcode:
dependency: transitive
description:
Expand Down Expand Up @@ -80,7 +80,7 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.11"
meta:
dependency: transitive
description:
Expand All @@ -94,7 +94,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.0.4"
version: "1.0.5"
path:
dependency: transitive
description:
Expand Down Expand Up @@ -155,7 +155,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
version: "0.4.3"
typed_data:
dependency: transitive
description:
Expand All @@ -169,7 +169,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
sdks:
dart: ">=2.12.0 <3.0.0"
dart: ">=2.14.0 <3.0.0"
flutter: ">=1.22.0"
183 changes: 150 additions & 33 deletions lib/paged_vertical_calendar.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart' hide DateUtils;
import 'package:flutter/rendering.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:intl/intl.dart';
import 'package:paged_vertical_calendar/utils/date_models.dart';
Expand Down Expand Up @@ -36,6 +37,7 @@ class PagedVerticalCalendar extends StatefulWidget {
this.physics,
this.scrollController,
this.listPadding = EdgeInsets.zero,
this.initialDate,
});

/// the [DateTime] to start the calendar from, if no [startDate] is provided
Expand Down Expand Up @@ -86,37 +88,126 @@ class PagedVerticalCalendar extends StatefulWidget {
/// scroll controller for making programmable scroll interactions
final ScrollController? scrollController;

/// with this date the calendar is initialized and the month of the date is displayed first.
/// If no initialDate is provided, today's date is taken.
final DateTime? initialDate;

@override
_PagedVerticalCalendarState createState() => _PagedVerticalCalendarState();
}

class _PagedVerticalCalendarState extends State<PagedVerticalCalendar> {
late PagingController<int, Month> controller;
late PagingController<int, Month> _pagingReplyUpController;
late PagingController<int, Month> _pagingReplyDownController;

final Key downListKey = UniqueKey();

late DateTime initDate;
late bool hideUp;

@override
void initState() {
super.initState();
controller = PagingController<int, Month>(

if (widget.initialDate != null) {
if (widget.endDate != null) {
int diffDaysEndDate =
widget.endDate!.difference(widget.initialDate!).inDays;
if (diffDaysEndDate.isNegative) {
initDate = widget.endDate!;
} else {
initDate = widget.initialDate!;
}
} else {
initDate = widget.initialDate!;
}
} else {
initDate = DateTime.now().removeTime();
}

if (widget.startDate != null) {
int diffDaysStartDate = widget.startDate!.difference(initDate).inDays;
print(diffDaysStartDate);
if (diffDaysStartDate.isNegative) {
hideUp = true;
} else {
hideUp = false;
}
} else {
hideUp = true;
}

_pagingReplyUpController = PagingController<int, Month>(
firstPageKey: 0,
invisibleItemsThreshold: widget.invisibleMonthsThreshold,
);
_pagingReplyUpController.addPageRequestListener(_fetchUpPage);
_pagingReplyUpController.addStatusListener(paginationStatusUp);

_pagingReplyDownController = PagingController<int, Month>(
firstPageKey: 0,
invisibleItemsThreshold: widget.invisibleMonthsThreshold,
);
controller.addPageRequestListener(fetchItems);
controller.addStatusListener(paginationStatus);
_pagingReplyDownController.addPageRequestListener(_fetchDownPage);
_pagingReplyDownController.addStatusListener(paginationStatusDown);
}

void paginationStatusUp(PagingStatus state) {
if (state == PagingStatus.completed)
return widget.onPaginationCompleted?.call();
}

void paginationStatus(PagingStatus state) {
void paginationStatusDown(PagingStatus state) {
if (state == PagingStatus.completed)
return widget.onPaginationCompleted?.call();
}

/// fetch a new [Month] object based on the [pageKey] which is the Nth month
/// from the start date
void fetchItems(int pageKey) async {
void _fetchUpPage(int pageKey) async {
// DateTime startDateUp = widget.startDate != null
// ? DateTime(widget.startDate!.year,
// widget.startDate!.month + initialIndex, widget.startDate!.day)
// : DateTime.now();

// DateTime initDateUp =
// Jiffy(DateTime(initialDate.year, initialDate.month, 1))
// .subtract(months: 1)
// .dateTime;

try {
final month = DateUtils.getMonth(
DateTime(initDate.year, initDate.month - 1, 1),
widget.startDate,
pageKey,
true);

WidgetsBinding.instance?.addPostFrameCallback(
(_) => widget.onMonthLoaded?.call(month.year, month.month),
);

final newItems = [month];
final isLastPage = widget.startDate != null &&
widget.startDate!.isSameDayOrAfter(month.weeks.first.firstDay);

if (isLastPage) {
return _pagingReplyUpController.appendLastPage(newItems);
}

final nextPageKey = pageKey + newItems.length;
_pagingReplyUpController.appendPage(newItems, nextPageKey);
} catch (_) {
_pagingReplyUpController.error;
}
}

void _fetchDownPage(int pageKey) async {
try {
final month = DateUtils.getMonth(
widget.startDate,
DateTime(initDate.year, initDate.month, 1),
widget.endDate,
pageKey,
false,
);

WidgetsBinding.instance?.addPostFrameCallback(
Expand All @@ -127,41 +218,63 @@ class _PagedVerticalCalendarState extends State<PagedVerticalCalendar> {
final isLastPage = widget.endDate != null &&
widget.endDate!.isSameDayOrBefore(month.weeks.last.lastDay);

if (isLastPage) return controller.appendLastPage(newItems);
if (isLastPage) {
return _pagingReplyDownController.appendLastPage(newItems);
}

final nextPageKey = pageKey + newItems.length;
controller.appendPage(newItems, nextPageKey);
_pagingReplyDownController.appendPage(newItems, nextPageKey);
} catch (_) {
controller.error;
_pagingReplyDownController.error;
}
}

@override
Widget build(BuildContext context) {
return SizedBox.expand(
child: PagedListView<int, Month>(
addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
padding: widget.listPadding,
pagingController: controller,
physics: widget.physics,
scrollController: widget.scrollController,
builderDelegate: PagedChildBuilderDelegate<Month>(
itemBuilder: (BuildContext context, Month month, int index) {
return _MonthView(
month: month,
monthBuilder: widget.monthBuilder,
dayBuilder: widget.dayBuilder,
onDayPressed: widget.onDayPressed,
);
},
),
),
return Scrollable(
viewportBuilder: (BuildContext context, ViewportOffset position) {
return Viewport(
offset: position,
center: downListKey,
slivers: [
if (hideUp)
PagedSliverList(
pagingController: _pagingReplyUpController,
builderDelegate: PagedChildBuilderDelegate<Month>(
itemBuilder: (BuildContext context, Month month, int index) {
return _MonthView(
month: month,
monthBuilder: widget.monthBuilder,
dayBuilder: widget.dayBuilder,
onDayPressed: widget.onDayPressed,
);
},
),
),
PagedSliverList(
key: downListKey,
pagingController: _pagingReplyDownController,
builderDelegate: PagedChildBuilderDelegate<Month>(
itemBuilder: (BuildContext context, Month month, int index) {
return _MonthView(
month: month,
monthBuilder: widget.monthBuilder,
dayBuilder: widget.dayBuilder,
onDayPressed: widget.onDayPressed,
);
},
),
),
],
);
},
);
}

@override
void dispose() {
controller.dispose();
_pagingReplyUpController.dispose();
_pagingReplyDownController.dispose();
super.dispose();
}
}
Expand All @@ -185,13 +298,18 @@ class _MonthView extends StatelessWidget {
children: <Widget>[
/// display the default month header if none is provided
monthBuilder?.call(context, month.month, month.year) ??
_DefaultMonthView(month: month.month, year: month.year),

_DefaultMonthView(
month: month.month,
year: month.year,
),
Table(
children: month.weeks.map((Week week) {
return _generateWeekRow(context, week);
}).toList(growable: false),
),
SizedBox(
height: 20,
),
],
);
}
Expand Down Expand Up @@ -249,9 +367,8 @@ class _DefaultMonthView extends StatelessWidget {

class _DefaultDayView extends StatelessWidget {
final DateTime date;
final bool? isSelected;

_DefaultDayView({required this.date, this.isSelected});
_DefaultDayView({required this.date});

@override
Widget build(BuildContext context) {
Expand Down
46 changes: 36 additions & 10 deletions lib/utils/date_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ import 'package:paged_vertical_calendar/utils/date_models.dart';

class DateUtils {
/// generates a [Month] object from the Nth index from the startdate
static Month getMonth(DateTime? minDate, DateTime? maxDate, int monthPage) {
static Month getMonth(
DateTime? minDate, DateTime? maxDate, int monthPage, bool up) {
// if no start date is provided use the current date
DateTime startDate = (minDate ?? DateTime.now()).removeTime();

// if this is not the first month in this calendar then calculate a new
// start date for this month
if (monthPage > 0) {
startDate = DateTime(startDate.year, startDate.month + monthPage, 1);
if (up) {
// fetsch up: month will be subtructed
startDate = DateTime(startDate.year, startDate.month - monthPage, 1);
} else {
// fetch down: month will be added
startDate = DateTime(startDate.year, startDate.month + monthPage, 1);
}
}

// find the first day of the first week in this month
Expand All @@ -29,16 +36,35 @@ class DateUtils {
// if an endDate is provided we need to check if the current week extends
// beyond this date. if it does, cap the week to the endDate and stop the
// loop
if (maxDate != null && lastDayOfWeek.isSameDayOrAfter(maxDate)) {
Week week = Week(firstDayOfWeek, maxDate);
weeks.add(week);
break;
}

Week week = Week(firstDayOfWeek, lastDayOfWeek);
weeks.add(week);
if (up) {
// fetching up
Week week;
if (maxDate != null && firstDayOfWeek.isBefore(maxDate)) {
week = Week(maxDate, lastDayOfWeek);
} else {
week = Week(firstDayOfWeek, lastDayOfWeek);
}

if (maxDate != null && lastDayOfWeek.isSameDayOrAfter(maxDate)) {
weeks.add(week);
} else if (maxDate == null) {
weeks.add(week);
}
if (week.isLastWeekOfMonth) break;
} else {
// fetching down
if (maxDate != null && lastDayOfWeek.isSameDayOrAfter(maxDate)) {
Week week = Week(firstDayOfWeek, maxDate);
weeks.add(week);
break;
}

Week week = Week(firstDayOfWeek, lastDayOfWeek);
weeks.add(week);

if (week.isLastWeekOfMonth) break;
if (week.isLastWeekOfMonth) break;
}

firstDayOfWeek = lastDayOfWeek.nextDay;
lastDayOfWeek = _lastDayOfWeek(firstDayOfWeek);
Expand Down
Loading

0 comments on commit 8b6cf2a

Please sign in to comment.