Skip to content

Support specifying inactive dates#395

Open
vanyasem wants to merge 2 commits intohyochan:mainfrom
vanyasem:sem-support-inactive-dates
Open

Support specifying inactive dates#395
vanyasem wants to merge 2 commits intohyochan:mainfrom
vanyasem:sem-support-inactive-dates

Conversation

@vanyasem
Copy link
Copy Markdown
Contributor

@vanyasem vanyasem commented Mar 19, 2025

Non-breaking change

Closes #381
Closes #286

@hyochan hyochan added the enhancement New feature or request label Mar 20, 2025
@hyochan hyochan requested a review from Copilot June 6, 2025 10:20
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds support for specifying dates that should be rendered and treated as inactive (unselectable) in the calendar widget.

  • Introduces a new inactiveDates property with a default empty list
  • Updates day rendering and tap handling to respect inactiveDates
  • Adds a DateTime.isSameDay extension to compare dates without time components
Comments suppressed due to low confidence (2)

lib/flutter_calendar_carousel.dart:128

  • Public API fields like inactiveDates should have doc comments explaining their purpose and behavior (e.g., how dates are matched and what rendering changes occur).
final List<DateTime> inactiveDates;

lib/flutter_calendar_carousel.dart:871

  • The new inactive-date blocking logic in _onDayPressed should have corresponding unit or widget tests to verify that taps on inactive dates do not select those days.
    if (widget.inactiveDates.any(

Comment thread lib/flutter_calendar_carousel.dart Outdated
Comment on lines 692 to 698
} else if (widget.inactiveDates.any(
(final DateTime inactiveDate) =>
inactiveDate.isSameDay(now),
)) {
isSelectable = false;
}

Copy link

Copilot AI Jun 6, 2025

Choose a reason for hiding this comment

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

The inactiveDates.any(...) check is duplicated in multiple rendering branches. Consider extracting this into a helper method (e.g. bool _isInactive(DateTime day)) to reduce duplication and improve readability.

Suggested change
} else if (widget.inactiveDates.any(
(final DateTime inactiveDate) =>
inactiveDate.isSameDay(now),
)) {
isSelectable = false;
}
} else if (_isInactive(now)) {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

addressed this in 0f997ca

Comment thread lib/flutter_calendar_carousel.dart Outdated
} else if (now.millisecondsSinceEpoch >
maxDate.millisecondsSinceEpoch) {
isSelectable = false;
} else if (widget.inactiveDates.any(
Copy link

Copilot AI Jun 6, 2025

Choose a reason for hiding this comment

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

Using List.any for each day render can be O(n) per cell; if inactiveDates grows large, consider storing dates in a Set or hashing normalized date keys for constant-time lookups.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

not going to address this

Copy link
Copy Markdown
Owner

@hyochan hyochan left a comment

Choose a reason for hiding this comment

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

@vanyasem Thank you again for the PR! Do you need this PR urgently? If you have a chance, I’d appreciate it if you could check Copilot’s review and let me know if there’s anything I can help address.

Thanks in advance!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 5, 2025

This PR is stale because it has been open 90 days with no activity. Leave a comment or this will be closed in 7 days

@vanyasem
Copy link
Copy Markdown
Contributor Author

vanyasem commented Sep 9, 2025

@hyochan sorry for the lengthly delay. I've replied to the Copilot's review. Would you mind taking an another look at the PR?

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 9, 2025

This PR is stale because it has been open 90 days with no activity. Leave a comment or this will be closed in 7 days

@vanyasem
Copy link
Copy Markdown
Contributor Author

not stale

@hyochan
Copy link
Copy Markdown
Owner

hyochan commented Apr 22, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an inactiveDates property to the CalendarCarousel widget, allowing specific dates to be disabled. The logic for determining date selectability has been refactored into a centralized _determineIsSelectable method, supported by a new DateTime extension. Review feedback suggests simplifying the _determineIsSelectable method with early returns to improve readability and potentially optimizing lookup performance with a Set. Additionally, a redundant check in the _onDayPressed handler was noted, with a suggestion to use the helper method for consistency.

Comment on lines +801 to +813
bool _determineIsSelectable(DateTime now) {
bool isSelectable = true;
if (now.millisecondsSinceEpoch < minDate.millisecondsSinceEpoch) {
isSelectable = false;
} else if (now.millisecondsSinceEpoch > maxDate.millisecondsSinceEpoch) {
isSelectable = false;
} else if (widget.inactiveDates.any(
(final DateTime inactiveDate) => inactiveDate.isSameDay(now),
)) {
isSelectable = false;
}
return isSelectable;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The _determineIsSelectable method can be simplified by using early returns and returning the result of the any check directly. This improves readability and reduces the need for a mutable variable.

Additionally, note that performing an any check on a List for every day rendered (35-42 times per month) results in O(N) complexity per day. If inactiveDates is expected to be large, consider pre-processing it into a Set of normalized values (e.g., in initState) to achieve O(1) lookups.

  bool _determineIsSelectable(DateTime now) {
    if (now.millisecondsSinceEpoch < minDate.millisecondsSinceEpoch) return false;
    if (now.millisecondsSinceEpoch > maxDate.millisecondsSinceEpoch) return false;
    return !widget.inactiveDates.any(
      (final DateTime inactiveDate) => inactiveDate.isSameDay(now),
    );
  }

Comment on lines +862 to +866
if (widget.inactiveDates.any(
(final DateTime inactiveDate) => inactiveDate.isSameDay(picked),
)) {
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This check is redundant because renderDay already uses isSelectable (which calls _determineIsSelectable) to set the onPressed callback to null for inactive dates. If you prefer to keep this as a defensive check, it should ideally call _determineIsSelectable(picked) to ensure consistency and avoid duplicating the logic.

@hyochan
Copy link
Copy Markdown
Owner

hyochan commented Apr 22, 2026

Bot review pass 1

  • Gemini (gemini-code-assist[bot]) — reviewed HEAD 0f997ca: 2 medium-priority, non-blocking suggestions.
    • lib/flutter_calendar_carousel.dart:813 — simplify _determineIsSelectable with early returns; consider Set<DateTime> for O(1) lookup if inactiveDates is large (currently O(N) per day × 35–42 days per month).
    • lib/flutter_calendar_carousel.dart:866_onDayPressed duplicates the inactiveDates check; prefer delegating to _determineIsSelectable for consistency.
  • Copilot (copilot-pull-request-reviewer[bot]) — unavailable: request-reviewer API returned 422 "Reviews may only be requested from collaborators." Historical Copilot review on prior SHA 6f59248 remains.
  • CodeRabbit (coderabbitai[bot]) — unavailable: no .coderabbit.yaml in the repo; app not installed.

No blockers. Merge is still gated by:

  1. CI has not run on this HEAD (0 check-runs, combined status pending). Approve the Actions workflow run on the PR's Checks tab to let Flutter CI execute.
  2. Prior maintainer CHANGES_REQUESTED review against SHA 6f59248 still counts toward the review decision; a fresh approval against 0f997ca is needed.

Classification: waiting-human (maintainer re-review + CI approval).

Copy link
Copy Markdown
Owner

@hyochan hyochan left a comment

Choose a reason for hiding this comment

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

Thanks — nice refactor to centralize the selectability check. A couple of blockers and polish items before merge.

Blocker — _onDayPressed duplicates the inactive check

After this diff, lib/flutter_calendar_carousel.dart has two copies of the "is this day blocked?" logic: _determineIsSelectable(...) used by the render paths, and an open-coded min/max + inactiveDates.any(...) inline in _onDayPressed (lines 857–864). Please consolidate — this is exactly what Gemini's medium-priority #2 was pointing at, and leaving both in place guarantees drift when the rules change.

Suggested fix:

void _onDayPressed(DateTime picked) {
  if (!_determineIsSelectable(picked)) return;
  setState(() {
    _selectedDate = picked;
  });
  widget.onDayPressed
      ?.call(picked, widget.markedDatesMap?.getEvents(picked) ?? const []);
}

Blocker — dartdoc on the new public parameter

Per our contribution rules, every new public parameter on CalendarCarousel needs a /// dartdoc comment. For inactiveDates please document both the intent and the timezone caveat, since isSameDay compares year/month/day on whatever calendar the DateTime is in:

/// Dates that should be rendered as non-selectable (pressing them is ignored).
///
/// Comparison is done by `year`, `month`, and `day`, so pass `DateTime` values
/// in the same timezone as the rest of your calendar data to avoid off-by-one
/// day mismatches across DST / UTC boundaries.
final List<DateTime> inactiveDates;

Polish

  • Name the extension. The anonymous extension on DateTime at the bottom of the file is file-scoped and works, but a name (e.g. _DateTimeSameDay) makes intent clearer and avoids any future surprise if the file grows more extensions.
  • Early returns in _determineIsSelectable. Minor; per Gemini's suggestion, you can turn the bool-accumulator pattern into straight return false; branches — purely readability.

Acknowledging the perf thread

Copilot's earlier List.anySet suggestion: I'm OK leaving this as-is for the common case (small inactiveDates, ~35–42 cells). If downstream users hit scale problems we can revisit in a follow-up.

Nice-to-have (not blocking)

A widget test that (a) renders a day listed in inactiveDates and confirms it's styled as non-selectable, and (b) taps it and confirms onDayPressed is not fired.

Push the fixes and I'll re-review. 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can I disable specific days?

3 participants