Skip to content
Open
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
5 changes: 4 additions & 1 deletion example/lib/pages/week_view_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ class _WeekViewDemoState extends State<WeekViewDemo> {
),
],
),
body: WeekViewWidget(heightPerMinute: _heightPerMinute),
body: WeekViewWidget(
heightPerMinute: _heightPerMinute,
enableVerticalWeekMode: true,
),
),
);
}
Expand Down
37 changes: 37 additions & 0 deletions example/lib/widgets/add_event_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
).applyDefaults(Theme.of(context).inputDecorationTheme),
initialDateTime: _startDate,
onSelect: (date) {
final previousStartDate = _startDate.withoutTime;

if (date.withoutTime.withoutTime.isAfter(
_endDate.withoutTime,
)) {
Expand All @@ -142,6 +144,18 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
if (_isRecurring) {
_endDate = _startDate;

if (mounted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {});
});
}
} else if (_endDate.withoutTime.isAtSameMomentAs(
previousStartDate,
)) {
// Keep single-day events aligned when the user changes
// only start date and has not explicitly changed end date.
_endDate = _startDate;

if (mounted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {});
Expand Down Expand Up @@ -521,6 +535,29 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {

_form.currentState?.save();

final hasOnlyOneTime =
(_startTime == null && _endTime != null) ||
(_startTime != null && _endTime == null);

if (hasOnlyOneTime) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please select both start time and end time.'),
),
);
return;
}

if (_startTime != null &&
_endTime != null &&
_startDate.withoutTime.isAtSameMomentAs(_endDate.withoutTime) &&
_endTime!.getTotalMinutes <= _startTime!.getTotalMinutes) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('End time must be after start time.')),
);
return;
}

DateTime? combinedStartTime;
DateTime? combinedEndTime;

Expand Down
4 changes: 2 additions & 2 deletions example/lib/widgets/calendar_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ class CalendarViews extends StatelessWidget {
color: AppColors.grey,
child: Center(
child: view == CalendarView.month
? MonthViewWidget(width: width)
? MonthViewWidget(width: width, enableVerticalMonthMode: true)
: view == CalendarView.day
? DayViewWidget(width: width)
: WeekViewWidget(width: width),
: WeekViewWidget(width: width, enableVerticalWeekMode: true),
),
);
}
Expand Down
11 changes: 10 additions & 1 deletion example/lib/widgets/month_view_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ import '../pages/event_details_page.dart';
class MonthViewWidget extends StatefulWidget {
final GlobalKey<MonthViewState>? state;
final double? width;
final bool enableVerticalMonthMode;

const MonthViewWidget({super.key, this.state, this.width});
const MonthViewWidget({
super.key,
this.state,
this.width,
this.enableVerticalMonthMode = false,
});

@override
State<MonthViewWidget> createState() => _MonthViewWidgetState();
Expand All @@ -34,6 +40,9 @@ class _MonthViewWidgetState extends State<MonthViewWidget> {
MonthView(
key: widget.state,
width: widget.width,
monthViewMode: widget.enableVerticalMonthMode
? MonthViewMode.verticalMonth
: MonthViewMode.standard,
selectedDate: _selectedDate,
multiDateSelectionRange: _multiSelectedDateRange,
multiDateSelectionColor: Colors.blue.withValues(alpha: 0.1),
Expand Down
11 changes: 11 additions & 0 deletions example/lib/widgets/week_view_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,30 @@ import '../pages/event_details_page.dart';
class WeekViewWidget extends StatelessWidget {
final GlobalKey<WeekViewState>? state;
final double? width;
final bool enableVerticalWeekMode;
final double heightPerMinute;

const WeekViewWidget({
super.key,
this.state,
this.width,
this.heightPerMinute = 1.0,
this.enableVerticalWeekMode = false,
});

@override
Widget build(BuildContext context) {
return WeekView(
key: state,
width: width,

// Set this to WeekViewMode.verticalWeek to enable:
// 1) fixed days on the left,
// 2) horizontal hour scrolling,
// 3) vertical week paging.
weekViewMode: enableVerticalWeekMode
? WeekViewMode.verticalWeek
: WeekViewMode.standard,
heightPerMinute: heightPerMinute,
showWeekends: true,
showMidnightHour: true,
Expand Down Expand Up @@ -69,6 +79,7 @@ class WeekViewWidget extends StatelessWidget {
SnackBar snackBar = SnackBar(content: Text("on LongTap"));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
weekTitleHeight: 72,
);
}
}
58 changes: 46 additions & 12 deletions lib/src/month_view/month_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import 'package:flutter/material.dart';
import '../../calendar_view.dart';
import '../extensions.dart';

/// Controls how [MonthView] pages are scrolled.
enum MonthViewMode {
/// Existing behavior: month pages scroll horizontally.
standard,

/// Month pages scroll vertically.
verticalMonth,
}

class MonthView<T extends Object?> extends StatefulWidget {
/// Main [Widget] to display month view.
const MonthView({
Expand All @@ -19,6 +28,7 @@ class MonthView<T extends Object?> extends StatefulWidget {
this.selectedDate,
this.multiDateSelectionRange = const {},
this.multiDateSelectionColor,
this.monthViewMode = MonthViewMode.standard,
}) : super(key: key);

/// A required parameters that controls events for month view.
Expand Down Expand Up @@ -58,6 +68,9 @@ class MonthView<T extends Object?> extends StatefulWidget {
/// Color of the date cells selected via [MonthViewBuilders.onDateLongPressMoveUpdate]
final Color? multiDateSelectionColor;

/// Controls scroll direction and layout behavior for the month pages.
final MonthViewMode monthViewMode;
Comment thread
kavantrivedi marked this conversation as resolved.

@override
MonthViewState<T> createState() => MonthViewState<T>();
}
Expand Down Expand Up @@ -217,21 +230,23 @@ class MonthViewState<T extends Object?> extends State<MonthView<T>> {
super.dispose();
}

void onHorizontalDragEnd(
void _onBoundaryDragEnd(
DragEndDetails dragEndDetails, {
required bool isFirstPage,
required bool isLastPage,
required Axis scrollDirection,
TextDirection textDirection = TextDirection.ltr,
}) {
final velocity = dragEndDetails.primaryVelocity ?? 0;
if (velocity == 0) return;

final isRtl = textDirection == TextDirection.rtl;

// In LTR: swipe right (velocity > 0) = previous, swipe left (velocity < 0) = next
// In RTL: swipe right (velocity > 0) = next, swipe left (velocity < 0) = previous
final isSwipingToPrevious = isRtl ? velocity < 0 : velocity > 0;
final isSwipingToNext = isRtl ? velocity > 0 : velocity < 0;
final isSwipingToPrevious = scrollDirection == Axis.horizontal
? (isRtl ? velocity < 0 : velocity > 0)
: velocity > 0;
final isSwipingToNext = scrollDirection == Axis.horizontal
? (isRtl ? velocity > 0 : velocity < 0)
: velocity < 0;

if (isFirstPage && isLastPage) {
// Only one page - trigger both callbacks based on swipe direction
Expand Down Expand Up @@ -281,6 +296,9 @@ class MonthViewState<T extends Object?> extends State<MonthView<T>> {
),
Expanded(
child: PageView.builder(
scrollDirection: widget.monthViewMode == MonthViewMode.standard
? Axis.horizontal
: Axis.vertical,
controller: _pageController,
physics: _isMultiDateSelectionInProgress
? const NeverScrollableScrollPhysics()
Expand Down Expand Up @@ -370,13 +388,29 @@ class MonthViewState<T extends Object?> extends State<MonthView<T>> {
final isFirstPage = index == 0;
final isLastPage = index == _totalMonths - 1;
if (isFirstPage || isLastPage) {
final axis =
widget.monthViewMode == MonthViewMode.standard
? Axis.horizontal
: Axis.vertical;
return GestureDetector(
onHorizontalDragEnd: (details) => onHorizontalDragEnd(
details,
isFirstPage: isFirstPage,
isLastPage: isLastPage,
textDirection: textDirection,
),
onHorizontalDragEnd: axis == Axis.horizontal
? (details) => _onBoundaryDragEnd(
details,
isFirstPage: isFirstPage,
isLastPage: isLastPage,
scrollDirection: axis,
textDirection: textDirection,
)
: null,
onVerticalDragEnd: axis == Axis.vertical
? (details) => _onBoundaryDragEnd(
details,
isFirstPage: isFirstPage,
isLastPage: isLastPage,
scrollDirection: axis,
textDirection: textDirection,
)
: null,
child: monthPageContent,
);
}
Expand Down
Loading
Loading