Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for RTL in HourLine Painters #265

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
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
6 changes: 5 additions & 1 deletion lib/src/components/_internal_components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,17 @@ class LiveTimeIndicator extends StatefulWidget {
/// Defines height occupied by one minute.
final double heightPerMinute;

final bool isRtl;

/// Widget to display tile line according to current time.
const LiveTimeIndicator(
{Key? key,
required this.width,
required this.height,
required this.timeLineWidth,
required this.liveTimeIndicatorSettings,
required this.heightPerMinute})
required this.heightPerMinute,
required this.isRtl})
: super(key: key);

@override
Expand Down Expand Up @@ -90,6 +93,7 @@ class _LiveTimeIndicatorState extends State<LiveTimeIndicator> {
widget.timeLineWidth + widget.liveTimeIndicatorSettings.offset,
_currentDate.getTotalMinutes * widget.heightPerMinute,
),
isRtl: widget.isRtl,
),
);
}
Expand Down
10 changes: 9 additions & 1 deletion lib/src/day_view/_internal_day_view_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class InternalDayViewPage<T extends Object?> extends StatelessWidget {
/// Emulate vertical line offset from hour line starts.
final double emulateVerticalOffsetBy;

final bool isRtl;

/// Defines a single day page.
const InternalDayViewPage({
Key? key,
Expand Down Expand Up @@ -145,6 +147,7 @@ class InternalDayViewPage<T extends Object?> extends StatelessWidget {
required this.halfHourIndicatorSettings,
required this.quarterHourIndicatorSettings,
required this.emulateVerticalOffsetBy,
required this.isRtl,
}) : super(key: key);

@override
Expand Down Expand Up @@ -179,6 +182,7 @@ class InternalDayViewPage<T extends Object?> extends StatelessWidget {
hourIndicatorSettings.dashWidth,
hourIndicatorSettings.dashSpaceWidth,
emulateVerticalOffsetBy,
isRtl: isRtl,
),
),
if (showHalfHours)
Expand Down Expand Up @@ -219,7 +223,10 @@ class InternalDayViewPage<T extends Object?> extends StatelessWidget {
minuteSlotSize: minuteSlotSize,
),
Align(
alignment: Alignment.centerRight,
alignment: isRtl
? Alignment.centerLeft
: Alignment
.centerRight, // adjust alignment based on directionality
child: EventGenerator<T>(
height: height,
date: date,
Expand Down Expand Up @@ -253,6 +260,7 @@ class InternalDayViewPage<T extends Object?> extends StatelessWidget {
height: height,
heightPerMinute: heightPerMinute,
timeLineWidth: timeLineWidth,
isRtl: isRtl,
),
),
],
Expand Down
107 changes: 44 additions & 63 deletions lib/src/day_view/day_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ class DayView<T extends Object?> extends StatefulWidget {
/// Emulate vertical line offset from hour line starts.
final double emulateVerticalOffsetBy;

/// Indicates if the layout direction is right-to-left (RTL).
///
/// This value is crucial for correctly drawing the table, especially
/// elements like the vertical line which change position based on
/// the layout direction. Pass this from parent contexts to ensure
/// accurate representation in RTL layouts.
final bool? isRtl;

/// Main widget for day view.
const DayView({
Key? key,
Expand Down Expand Up @@ -258,16 +266,12 @@ class DayView<T extends Object?> extends StatefulWidget {
this.startDuration = const Duration(hours: 0),
this.onHeaderTitleTap,
this.emulateVerticalOffsetBy = 0,
}) : assert(!(onHeaderTitleTap != null && dayTitleBuilder != null),
"can't use [onHeaderTitleTap] & [dayTitleBuilder] simultaneously"),
assert(timeLineOffset >= 0,
"timeLineOffset must be greater than or equal to 0"),
assert(width == null || width > 0,
"Calendar width must be greater than 0."),
assert(timeLineWidth == null || timeLineWidth > 0,
"Time line width must be greater than 0."),
assert(
heightPerMinute > 0, "Height per minute must be greater than 0."),
this.isRtl,
}) : assert(!(onHeaderTitleTap != null && dayTitleBuilder != null), "can't use [onHeaderTitleTap] & [dayTitleBuilder] simultaneously"),
assert(timeLineOffset >= 0, "timeLineOffset must be greater than or equal to 0"),
assert(width == null || width > 0, "Calendar width must be greater than 0."),
assert(timeLineWidth == null || timeLineWidth > 0, "Time line width must be greater than 0."),
assert(heightPerMinute > 0, "Height per minute must be greater than 0."),
assert(
dayDetectorBuilder == null || onDateLongPress == null,
"""If you use [dayPressDetectorBuilder]
Expand Down Expand Up @@ -321,9 +325,12 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {

final _scrollConfiguration = EventScrollConfiguration<T>();

late bool isRtl;

@override
void initState() {
super.initState();
isRtl = widget.isRtl ?? Directionality.of(context) == TextDirection.rtl;

_reloadCallback = _reload;
_setDateRange();
Expand All @@ -333,9 +340,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
_regulateCurrentDate();

_calculateHeights();
_scrollController = ScrollController(
initialScrollOffset: widget.scrollOffset ??
widget.startDuration.inMinutes * widget.heightPerMinute);
_scrollController = ScrollController(initialScrollOffset: widget.scrollOffset ?? widget.startDuration.inMinutes * widget.heightPerMinute);
_pageController = PageController(initialPage: _currentIndex);
_eventArranger = widget.eventArranger ?? SideEventArranger<T>();
_assignBuilders();
Expand All @@ -345,8 +350,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
void didChangeDependencies() {
super.didChangeDependencies();

final newController = widget.controller ??
CalendarControllerProvider.of<T>(context).controller;
final newController = widget.controller ?? CalendarControllerProvider.of<T>(context).controller;

if (newController != _controller) {
_controller = newController;
Expand All @@ -365,8 +369,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
void didUpdateWidget(DayView<T> oldWidget) {
super.didUpdateWidget(oldWidget);
// Update controller.
final newController = widget.controller ??
CalendarControllerProvider.of<T>(context).controller;
final newController = widget.controller ?? CalendarControllerProvider.of<T>(context).controller;

if (newController != _controller) {
_controller?.removeListener(_reloadCallback);
Expand All @@ -375,8 +378,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
}

// Update date range.
if (widget.minDay != oldWidget.minDay ||
widget.maxDay != oldWidget.maxDay) {
if (widget.minDay != oldWidget.minDay || widget.maxDay != oldWidget.maxDay) {
_setDateRange();
_regulateCurrentDate();

Expand Down Expand Up @@ -424,16 +426,13 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
controller: _pageController,
onPageChanged: _onPageChange,
itemBuilder: (_, index) {
final date = DateTime(_minDate.year, _minDate.month,
_minDate.day + index);
final date = DateTime(_minDate.year, _minDate.month, _minDate.day + index);
return ValueListenableBuilder(
valueListenable: _scrollConfiguration,
builder: (_, __, ___) => InternalDayViewPage<T>(
key: ValueKey(
_hourHeight.toString() + date.toString()),
key: ValueKey(_hourHeight.toString() + date.toString()),
width: _width,
liveTimeIndicatorSettings:
_liveTimeIndicatorSettings,
liveTimeIndicatorSettings: _liveTimeIndicatorSettings,
timeLineBuilder: _timeLineBuilder,
dayDetectorBuilder: _dayDetectorBuilder,
eventTileBuilder: _eventTileBuilder,
Expand All @@ -444,8 +443,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
onTileTap: widget.onEventTap,
onDateLongPress: widget.onDateLongPress,
onDateTap: widget.onDateTap,
showLiveLine: widget.showLiveTimeLineInAllDays ||
date.compareWithoutTime(DateTime.now()),
showLiveLine: widget.showLiveTimeLineInAllDays || date.compareWithoutTime(DateTime.now()),
timeLineOffset: widget.timeLineOffset,
timeLineWidth: _timeLineWidth,
verticalLineOffset: widget.verticalLineOffset,
Expand All @@ -460,12 +458,10 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
scrollController: _scrollController,
showHalfHours: widget.showHalfHours,
showQuarterHours: widget.showQuarterHours,
halfHourIndicatorSettings:
_halfHourIndicatorSettings,
quarterHourIndicatorSettings:
_quarterHourIndicatorSettings,
emulateVerticalOffsetBy:
widget.emulateVerticalOffsetBy,
halfHourIndicatorSettings: _halfHourIndicatorSettings,
quarterHourIndicatorSettings: _quarterHourIndicatorSettings,
emulateVerticalOffsetBy: widget.emulateVerticalOffsetBy,
isRtl: isRtl,
),
);
},
Expand Down Expand Up @@ -511,8 +507,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
offset: 5 + widget.verticalLineOffset,
);

assert(_liveTimeIndicatorSettings.height < _hourHeight,
"liveTimeIndicator height must be less than minuteHeight * 60");
assert(_liveTimeIndicatorSettings.height < _hourHeight, "liveTimeIndicator height must be less than minuteHeight * 60");

_hourIndicatorSettings = widget.hourIndicatorSettings ??
HourIndicatorSettings(
Expand All @@ -521,8 +516,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
offset: 5,
);

assert(_hourIndicatorSettings.height < _hourHeight,
"hourIndicator height must be less than minuteHeight * 60");
assert(_hourIndicatorSettings.height < _hourHeight, "hourIndicator height must be less than minuteHeight * 60");

_halfHourIndicatorSettings = widget.halfHourIndicatorSettings ??
HourIndicatorSettings(
Expand All @@ -531,8 +525,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
offset: 5,
);

assert(_halfHourIndicatorSettings.height < _hourHeight,
"halfHourIndicator height must be less than minuteHeight * 60");
assert(_halfHourIndicatorSettings.height < _hourHeight, "halfHourIndicator height must be less than minuteHeight * 60");

_quarterHourIndicatorSettings = widget.quarterHourIndicatorSettings ??
HourIndicatorSettings(
Expand All @@ -541,8 +534,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
offset: 5,
);

assert(_quarterHourIndicatorSettings.height < _hourHeight,
"quarterHourIndicator height must be less than minuteHeight * 60");
assert(_quarterHourIndicatorSettings.height < _hourHeight, "quarterHourIndicator height must be less than minuteHeight * 60");
}

void _calculateHeights() {
Expand All @@ -554,10 +546,8 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
_timeLineBuilder = widget.timeLineBuilder ?? _defaultTimeLineBuilder;
_eventTileBuilder = widget.eventTileBuilder ?? _defaultEventTileBuilder;
_dayTitleBuilder = widget.dayTitleBuilder ?? _defaultDayBuilder;
_fullDayEventBuilder =
widget.fullDayEventBuilder ?? _defaultFullDayEventBuilder;
_dayDetectorBuilder =
widget.dayDetectorBuilder ?? _defaultPressDetectorBuilder;
_fullDayEventBuilder = widget.fullDayEventBuilder ?? _defaultFullDayEventBuilder;
_dayDetectorBuilder = widget.dayDetectorBuilder ?? _defaultPressDetectorBuilder;
_hourLinePainter = widget.hourLinePainter ?? _defaultHourLinePainter;
}

Expand Down Expand Up @@ -648,8 +638,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
/// Default timeline builder this builder will be used if
/// [widget.eventTileBuilder] is null
///
Widget _defaultTimeLineBuilder(date) => DefaultTimeLineMark(
date: date, timeStringBuilder: widget.timeStringBuilder);
Widget _defaultTimeLineBuilder(date) => DefaultTimeLineMark(date: date, timeStringBuilder: widget.timeStringBuilder);

/// Default timeline builder. This builder will be used if
/// [widget.eventTileBuilder] is null
Expand Down Expand Up @@ -705,9 +694,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
);
}

Widget _defaultFullDayEventBuilder(
List<CalendarEventData<T>> events, DateTime date) =>
FullDayEventView(events: events, date: date);
Widget _defaultFullDayEventBuilder(List<CalendarEventData<T>> events, DateTime date) => FullDayEventView(events: events, date: date);

HourLinePainter _defaultHourLinePainter(
Color lineColor,
Expand All @@ -732,6 +719,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
dashWidth: dashWidth,
dashSpaceWidth: dashSpaceWidth,
emulateVerticalOffsetBy: emulateVerticalOffsetBy,
isRtl: isRtl,
);
}

Expand Down Expand Up @@ -771,8 +759,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
/// respectively.
///
///
void previousPage({Duration? duration, Curve? curve}) =>
_pageController.previousPage(
void previousPage({Duration? duration, Curve? curve}) => _pageController.previousPage(
duration: duration ?? widget.pageTransitionDuration,
curve: curve ?? widget.pageTransitionCurve,
);
Expand All @@ -790,9 +777,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
///
///
Future<void> animateToPage(int page, {Duration? duration, Curve? curve}) =>
_pageController.animateToPage(page,
duration: duration ?? widget.pageTransitionDuration,
curve: curve ?? widget.pageTransitionCurve);
_pageController.animateToPage(page, duration: duration ?? widget.pageTransitionDuration, curve: curve ?? widget.pageTransitionCurve);

/// Returns current page number.
///
Expand All @@ -816,8 +801,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
/// respectively.
///
///
Future<void> animateToDate(DateTime date,
{Duration? duration, Curve? curve}) async {
Future<void> animateToDate(DateTime date, {Duration? duration, Curve? curve}) async {
if (date.isBefore(_minDate) || date.isAfter(_maxDate)) {
throw "Invalid date selected.";
}
Expand Down Expand Up @@ -855,8 +839,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
/// scroll to event tile.
///
///
Future<void> animateToEvent(CalendarEventData<T> event,
{Duration? duration, Curve? curve}) async {
Future<void> animateToEvent(CalendarEventData<T> event, {Duration? duration, Curve? curve}) async {
await animateToDate(event.date, duration: duration, curve: curve);
await _scrollConfiguration.setScrollEvent(
event: event,
Expand All @@ -876,8 +859,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {

// Added ternary condition below to take care if user passing duration
// above 24 hrs then we take it max as 24 hours only
final offset = offSetForSingleMinute *
(startDurationInMinutes > 3600 ? 3600 : startDurationInMinutes);
final offset = offSetForSingleMinute * (startDurationInMinutes > 3600 ? 3600 : startDurationInMinutes);
animateTo(
offset.toDouble(),
duration: duration,
Expand All @@ -899,6 +881,5 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
}

/// Returns the current visible date in day view.
DateTime get currentDate =>
DateTime(_currentDate.year, _currentDate.month, _currentDate.day);
DateTime get currentDate => DateTime(_currentDate.year, _currentDate.month, _currentDate.day);
}
Loading
Loading