JonasWanke/timetable

Change color of currently selected day in CompactMonthTimetable

Opened this issue · 18 comments

How can currently selected date in CompactMonthTimetable be changed?

In a way so that today date is always selected in its own colour (as it is now which is good), but for those manually selected dates in CompactMonthTimetable to set some custom color?

image

If you already customize the dateBuilder of MonthWidget, you can customize/overwrite the style for each DateIndicator individually. Alternatively, you can customize the themeData.dateIndicatorStyleProvider and return the style you want for each day.

@JonasWanke I will try it tomorrow morning with customized dateBuilder.

I have one more question related to dateBuilder style, when i applied custom dateBuilder i noticed dates from other months are not displayed in MonthWidget (neither as "enabled" or "disabled"), but only dates from currently displayed month are shown? For details i will post screenshot and code tomorrow morning

What i would like is to show dates from other months in a light grey color just as they are currently in default settings ( although if you notice image above in first post here (default display) you will see that dates that are before "today" date are in different color compared to those dates that are after "today" date, as if they are lighter color, and disabled dates are even lighter but that is good about disabled dates. Is that default style or i have perhaps done something wrong?

Have a look at monthWidgetStyle.showDatesFromOtherMonths and monthWidgetStyle.showDatesFromOtherMonthsAsDisabled, which are used here:

dateBuilder = dateBuilder ??
((context, date) {
assert(date.isValidTimetableDate);
final timetableTheme = TimetableTheme.orDefaultOf(context);
DateIndicatorStyle? dateStyle;
if (date.firstDayOfMonth != month &&
(style ?? timetableTheme.monthWidgetStyleProvider(month))
.showDatesFromOtherMonthsAsDisabled) {
final original =
timetableTheme.dateIndicatorStyleProvider(date);
dateStyle = original.copyWith(
textStyle: original.textStyle.copyWith(
color: context.theme.colorScheme.background.disabledOnColor,
),
);
}
return DateIndicator(date, style: dateStyle);
});

@JonasWanke I'm trying handle MonthWidget now and I run into few issues that i'm trying to solve:

  1. FIRST ISSUE:
    I'm not sure how to define color for currently selected date, as you suggested I tried to use dateIndicatorStyleProvider but I have to select date 2 times in order for changes to be applied visually, here is a short clip:
dates.issue.mp4

CODE:

    dateIndicatorStyleProvider: (date) => DateIndicatorStyle(
      context,
      date,
      textStyle: TextStyle(color: _dateController.date.value == date ? Colors.white : Colors.black),
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        color: _dateController.date.value == date ? Colors.red : Colors.transparent
        ),
      ),
  1. SECOND ISSUE:
    I have navigated to January 2022, and there only 30 dates appears when it should be 31. I have also noticed same issue with February 2022 where it shows only 27 days instead of 28:

image
image

Regarding the first issue: Is the underlying Date controller updated and only that part of the UI isn't updated? It might be a problem with your state management: You have to rebuild the widget for it to know that the lambda will return a different value.

Regarding the second issue: Please file a separate issue for that. If the problem isn't reproducible, please also include the relevant subset of your code.

Is the underlying Date controller updated and only that part of the UI isn't updated?

I haven't done my own coding here, I have tried to use what I already have and I see there is a setState in onDateTap callback (in _updateVisibleDateRange method):

child: Builder(builder: (context) {
  return DefaultTimetableCallbacks(
    callbacks: DefaultTimetableCallbacks.of(context)!.copyWith(
      onDateTap: (date) {
        _dateController.animateTo(date, vsync: this);
        _updateVisibleDateRange(PredefinedVisibleDateRange.day);
      },
    ),

where _updateVisibleDateRange is:

    void _updateVisibleDateRange(PredefinedVisibleDateRange newValue) {
    setState(() {
      _visibleDateRange = newValue;
      _dateController.visibleRange = newValue.visibleDateRange;
    });

_dateController.animateTo changes the value asynchronously. You can try to call setState after the animation is done

@JonasWanke I have tried this way and it did worked:

onDateTap: (date) {
  _dateController.animateTo(date, vsync: this).then(
        (value) => _updateVisibleDateRange(
            PredefinedVisibleDateRange.day),
      );
},
Recording.18.mp4

It does work but it changes only when animation completes with delay (does not feel natural as it is with other calendars). Is there any way to make it so that it instantly changes style for newly selected date?

You could use a separate DateController for that on which you call jump instead of animate. Alternatively, change the focus date and compare to it in the lambda you pass to the style.

focus date and compare to it in the lambda you pass to the style.

What would be focus date and how could i change it? And in this case with focus date i would not have to use .then function to change focus date after animation completes?

onDateTap: (date) {
  _dateController.animateTo(date, vsync: this).then(
        (value) => _updateVisibleDateRange(
            PredefinedVisibleDateRange.day),
      );
},

A separate field next to the controller that you always set to the date that should be highlighted.

Exactly. Just set it synchronously before or after calling animate and then call setState synchronously as well

A separate field next to the controller that you always set to the date that should be highlighted

Which separate field? Isn't is from a function _updateVisibleDateRange ? I do not understand how exactly is selected date highligted

  void _updateVisibleDateRange(PredefinedVisibleDateRange newValue) {
    setState(() {
      _visibleDateRange = newValue;
      _dateController.visibleRange = newValue.visibleDateRange;
    });
  }

According to #100 (comment), you do the highlighting yourself. Instead of basing that on _dateController.date.value, you can create a separate field for storing the date that should be displayed as selected

_dateController.date.value

oh a regular state value, thank you I tried and it works!

timetable/lib/src/components/month_widget.dart

I have removed themeMode: overrideState.themeMode from Material App and I use my own theme, and now i'm trying to override CompactMonthTimetable's MonthWidget, specifically:

  CompactMonthTimetable(
monthBuilder: (context, month) => Material(
  color: Colors.white,
  child: MonthWidget(
    month,
    dateBuilder: (context, date) {
      assert(date.isValidTimetableDate);

      return DateIndicator(
        date,
        style: DateIndicatorStyle(
          context,
          date,
          textStyle: TextStyle(
            color: selectedDate == date
                ? Colors.white
                : Colors.black,
          ),
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            color: selectedDate == date
                ? AppColors.primaryColor
                : Colors.transparent,
          ),
        ),
      );
    },
    weekBuilder: (context, week) => SizedBox.shrink(),
  ),
),
),

the issue is that now I don't know how to set dates from other months as disabled (different color)?
image

You can check whether date.firstDayOfMonth == month and set the text style based on that

You can check whether date.firstDayOfMonth == month and set the text style based on that

Thanks for reply I will check it tomorrow morning and reply back here

style: DateIndicatorStyle(
  context,
  date,
  textStyle: TextStyle(
    color: selectedDate == date
        ? Colors.white
        : date.firstDayOfMonth == month ? Colors.black : Colors.grey[500],
  ),

Worked! Thank you @JonasWanke