Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
## 2.6.0

* SmartParser — Gujarati, Bengali, Tamil parsing
* SmartParser — new English expressions:
* "first/last monday of month"
* "2 mondays ago", "in 2 fridays"
* "beginning/end of next month"
* "this coming monday"
* "q1", "q2", "quarter 3"
* SmartCalendar — range selection mode
* `rangeSelectionMode` param
* `onRangeSelected` callback
* Visual range highlight in month view
* SmartCalendar — event overlap detection in week view
* `supportedParseLocales` updated — now includes gu, bn, ta

## 2.5.0

- Added `themeMode` to SmartCalendar — light/dark/system
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
<a href="https://github.com/harshyadavDeveloper/smart_date_formatter/actions">
<img src="https://github.com/harshyadavDeveloper/smart_date_formatter/actions/workflows/ci.yml/badge.svg" alt="CI"/>
</a>
<a href="https://pub.dev/packages/smart_date_formatter/score">
<img src="https://img.shields.io/pub/points/smart_date_formatter?color=2E8B57&label=pub%20points" alt="Pub Points"/>
</a>
</p>

<p align="center">
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ packages:
path: ".."
relative: true
source: path
version: "2.3.0"
version: "2.5.0"
source_span:
dependency: transitive
description:
Expand Down
70 changes: 68 additions & 2 deletions lib/src/calendar/smart_calendar.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:smart_date_formatter/smart_date_formatter.dart';
import 'package:smart_date_formatter/src/calendar/views/agenda_view.dart';
import 'calendar_event.dart';
import 'calendar_controller.dart';
import 'views/month_view.dart';
import 'views/week_view.dart';
import 'views/day_view.dart';
Expand Down Expand Up @@ -96,6 +95,12 @@ class SmartCalendar extends StatefulWidget {
/// Theme mode for calendar
final ThemeMode themeMode;

/// Enable range selection mode
final bool rangeSelectionMode;

/// Called when range is selected in calendar
final void Function(DateTime start, DateTime end)? onRangeSelected;

/// Custom cell builder for month view dates.
///
/// ```dart
Expand Down Expand Up @@ -155,6 +160,8 @@ class SmartCalendar extends StatefulWidget {
this.showWeekNumbers = false,
this.themeMode = ThemeMode.light,
this.cellBuilder,
this.rangeSelectionMode = false,
this.onRangeSelected,
});

@override
Expand All @@ -165,6 +172,9 @@ class _SmartCalendarState extends State<SmartCalendar> {
late SmartCalendarController _controller;
late CalendarView _currentView;
bool _ownsController = false;
DateTime? _rangeStart;
DateTime? _rangeEnd;
// bool _selectingRangeStart = true;

@override
void initState() {
Expand Down Expand Up @@ -204,6 +214,26 @@ class _SmartCalendarState extends State<SmartCalendar> {
Color get _subtleTextColor =>
_isDark ? Colors.grey.shade400 : Colors.grey.shade600;

// void _handleRangeTap(DateTime date) {
// setState(() {
// if (_selectingRangeStart || _rangeEnd != null) {
// _rangeStart = date;
// _rangeEnd = null;
// _selectingRangeStart = false;
// } else {
// if (date.isBefore(_rangeStart!)) {
// _rangeEnd = _rangeStart;
// _rangeStart = date;
// } else {
// _rangeEnd = date;
// }
// _selectingRangeStart = true;
// widget.onRangeSelected?.call(_rangeStart!, _rangeEnd!);
// }
// _controller.selectDate(date);
// });
// }

@override
void dispose() {
if (_ownsController) _controller.dispose();
Expand Down Expand Up @@ -240,6 +270,40 @@ class _SmartCalendarState extends State<SmartCalendar> {
),
],
),
// After view switcher row
if (widget.rangeSelectionMode) ...[
Container(
margin: const EdgeInsets.only(bottom: 8),
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: widget.selectedColor.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.date_range,
size: 14, color: widget.selectedColor),
const SizedBox(width: 6),
Text(
_rangeStart == null
? 'Tap to select start date'
: _rangeEnd == null
? 'Tap to select end date'
: '${_rangeStart!.format('dd MMM')} → '
'${_rangeEnd!.format('dd MMM')} '
'(${_rangeEnd!.daysUntil(_rangeStart!) * -1 + 1} days)',
style: TextStyle(
fontSize: 12,
color: widget.selectedColor,
fontWeight: FontWeight.w500,
),
),
],
),
),
],

// Calendar view
_buildCurrentView(),
Expand Down Expand Up @@ -343,6 +407,8 @@ class _SmartCalendarState extends State<SmartCalendar> {
showWeekNumbers: widget.showWeekNumbers,
isDark: _isDark,
cellBuilder: widget.cellBuilder,
rangeStart: widget.rangeSelectionMode ? _rangeStart : null, // 👈 new
rangeEnd: widget.rangeSelectionMode ? _rangeEnd : null,
);
case CalendarView.week:
return WeekView(
Expand Down
95 changes: 79 additions & 16 deletions lib/src/calendar/views/month_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ class MonthView extends StatelessWidget {
/// Whether dark theme is active
final bool isDark;

/// Range selection start date
final DateTime? rangeStart;

/// Range selection end date
final DateTime? rangeEnd;

/// Custom cell builder for month view dates.
///
/// ```dart
Expand Down Expand Up @@ -94,6 +100,8 @@ class MonthView extends StatelessWidget {
this.showWeekNumbers = false,
this.isDark = false,
this.cellBuilder,
this.rangeStart,
this.rangeEnd,
});

List<DateTime> _daysInMonth(DateTime month) {
Expand Down Expand Up @@ -243,22 +251,8 @@ class MonthView extends StatelessWidget {
isToday,
),
)
: DayCell(
date: date,
events: dayEvents,
isSelected: isSelected,
isToday: isToday,
isCurrentMonth: isCurrentMonth,
isWeekend: isWeekend,
markerStyle: markerStyle,
selectedColor: selectedColor,
todayColor: todayColor,
isDark: isDark,
onTap: () {
controller.selectDate(date);
onDateSelected?.call(date, dayEvents);
},
),
: _buildRangeCell(date, dayEvents, isSelected, isToday,
isCurrentMonth, isWeekend),
);
}),
],
Expand All @@ -272,4 +266,73 @@ class MonthView extends StatelessWidget {
final dayOfYear = date.difference(firstDayOfYear).inDays + 1;
return ((dayOfYear + firstDayOfYear.weekday - 2) / 7).ceil().clamp(1, 53);
}

bool _isInSelectedRange(DateTime date) {
if (rangeStart == null || rangeEnd == null) return false;
final d = DateTime(date.year, date.month, date.day);
final s = DateTime(rangeStart!.year, rangeStart!.month, rangeStart!.day);
final e = DateTime(rangeEnd!.year, rangeEnd!.month, rangeEnd!.day);
return d.isAfter(s) && d.isBefore(e);
}

bool _isRangeStart(DateTime date) =>
rangeStart != null && date.isSameDay(rangeStart!);

bool _isRangeEnd(DateTime date) =>
rangeEnd != null && date.isSameDay(rangeEnd!);

Widget _buildRangeCell(
DateTime date,
List<CalendarEvent> events,
bool isSelected,
bool isToday,
bool isCurrentMonth,
bool isWeekend,
) {
final inRange = _isInSelectedRange(date);
final isStart = _isRangeStart(date);
final isEnd = _isRangeEnd(date);

return GestureDetector(
onTap: () {
controller.selectDate(date);
onDateSelected?.call(date, events);
},
child: Container(
margin: EdgeInsets.zero,
decoration: BoxDecoration(
color: isStart || isEnd
? selectedColor
: inRange
? selectedColor.withValues(alpha: 0.12)
: Colors.transparent,
borderRadius: isStart
? const BorderRadius.only(
topLeft: Radius.circular(8),
bottomLeft: Radius.circular(8),
)
: isEnd
? const BorderRadius.only(
topRight: Radius.circular(8),
bottomRight: Radius.circular(8),
)
: inRange
? BorderRadius.zero
: BorderRadius.circular(8),
),
child: DayCell(
date: date,
events: events,
isSelected: isSelected || isStart || isEnd,
isToday: isToday,
isCurrentMonth: isCurrentMonth,
isWeekend: isWeekend,
markerStyle: markerStyle,
selectedColor: selectedColor,
todayColor: todayColor,
isDark: isDark,
),
),
);
}
}
Loading
Loading