From 5c30cc0e1b527a6ed1a0caa220eb1d08bb209ad1 Mon Sep 17 00:00:00 2001 From: Shubham Jitiya Date: Thu, 9 Jan 2025 18:41:08 +0530 Subject: [PATCH] =?UTF-8?q?feat:=20Fixes=20issue=20#412:=20=E2=9C=A8=20Add?= =?UTF-8?q?=20support=20for=20RTL=20directionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + README.md | 23 ++- example/lib/constants.dart | 1 + example/lib/widgets/add_event_form.dart | 28 ++-- example/lib/widgets/calendar_configs.dart | 5 +- example/lib/widgets/day_view_widget.dart | 14 +- example/lib/widgets/week_view_widget.dart | 33 ++++- lib/src/components/_internal_components.dart | 9 +- lib/src/components/day_view_components.dart | 36 +++-- lib/src/constants.dart | 1 + lib/src/day_view/_internal_day_view_page.dart | 7 +- lib/src/day_view/day_view.dart | 6 +- .../event_arrangers/side_event_arranger.dart | 10 +- lib/src/painters.dart | 139 +++++++++++++++--- .../week_view/_internal_week_view_page.dart | 54 ++++--- lib/src/week_view/week_view.dart | 1 + 16 files changed, 284 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 982d4db6..32daecc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # [1.4.1 - Unreleased] - Adds clear method to `EventController`. +- Adds support of directionality. [#412](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/issues/412) # [1.4.0 - 7 Jan 2025](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/tree/1.4.0) diff --git a/README.md b/README.md index 4984a0ee..1db83c2c 100644 --- a/README.md +++ b/README.md @@ -352,9 +352,30 @@ WeekView( ], ); ``` - Above code will create `WeekView` with only five days, from monday to friday. +### Support for RTL +Wrap your widget with `Directionality` widget and use `textDirection` to give RTL or LTR direction. + +```dart +Directionality( + textDirection: TextDirection.rtl, + child: ResponsiveWidget( + webWidget: WebHomePage( + selectedView: CalendarView.week, + ), + mobileWidget: Scaffold( + floatingActionButton: FloatingActionButton( + child: Icon(Icons.add), + elevation: 8, + onPressed: () => context.pushRoute(CreateEventPage()), + ), + body: WeekViewWidget(), + ), + ), +); +``` + ## Main Contributors diff --git a/example/lib/constants.dart b/example/lib/constants.dart index 21da78d8..adc9aa4b 100644 --- a/example/lib/constants.dart +++ b/example/lib/constants.dart @@ -6,6 +6,7 @@ class AppConstants { AppConstants._(); static final List weekTitles = ['M', 'T', 'W', 'T', 'F', 'S', 'S']; + static final ltr = '\u202A'; // Use this to force text direction to LTR static OutlineInputBorder inputBorder = OutlineInputBorder( borderRadius: BorderRadius.circular(7), diff --git a/example/lib/widgets/add_event_form.dart b/example/lib/widgets/add_event_form.dart index dbdf31c9..e7888d77 100644 --- a/example/lib/widgets/add_event_form.dart +++ b/example/lib/widgets/add_event_form.dart @@ -67,6 +67,7 @@ class _AddOrEditEventFormState extends State { key: _form, child: Column( mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( controller: _titleController, @@ -255,15 +256,12 @@ class _AddOrEditEventFormState extends State { hintText: "Event Description", ), ), - Align( - alignment: Alignment.centerLeft, - child: Text( - 'Repeat', - style: TextStyle( - color: AppColors.black, - fontWeight: FontWeight.w500, - fontSize: 17, - ), + Text( + 'Repeat', + style: TextStyle( + color: AppColors.black, + fontWeight: FontWeight.w500, + fontSize: 17, ), ), Row( @@ -392,7 +390,7 @@ class _AddOrEditEventFormState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Reoccurrence ends on: ', + '${AppConstants.ltr}Reoccurrence ends on: ', style: TextStyle( color: AppColors.black, fontWeight: FontWeight.w500, @@ -519,7 +517,7 @@ class _AddOrEditEventFormState extends State { Row( children: [ Text( - "Event Color: ", + "${AppConstants.ltr}Event Color: ", style: TextStyle( color: AppColors.black, fontSize: 17, @@ -537,9 +535,11 @@ class _AddOrEditEventFormState extends State { SizedBox( height: 15, ), - CustomButton( - onTap: _createEvent, - title: widget.event == null ? "Add Event" : "Update Event", + Center( + child: CustomButton( + onTap: _createEvent, + title: widget.event == null ? "Add Event" : "Update Event", + ), ), ], ), diff --git a/example/lib/widgets/calendar_configs.dart b/example/lib/widgets/calendar_configs.dart index 9f976f88..f45ac9b0 100644 --- a/example/lib/widgets/calendar_configs.dart +++ b/example/lib/widgets/calendar_configs.dart @@ -1,4 +1,5 @@ import 'package:calendar_view/calendar_view.dart'; +import 'package:example/constants.dart'; import 'package:flutter/material.dart'; import '../app_colors.dart'; @@ -43,7 +44,7 @@ class CalendarConfig extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Active View:", + "${AppConstants.ltr}Active View:", style: TextStyle( fontSize: 20.0, color: AppColors.black, @@ -89,7 +90,7 @@ class CalendarConfig extends StatelessWidget { height: 40, ), Text( - "Add Event: ", + "${AppConstants.ltr}Add Event: ", style: TextStyle( fontSize: 20.0, color: AppColors.black, diff --git a/example/lib/widgets/day_view_widget.dart b/example/lib/widgets/day_view_widget.dart index a02c189f..ba52d41c 100644 --- a/example/lib/widgets/day_view_widget.dart +++ b/example/lib/widgets/day_view_widget.dart @@ -1,4 +1,5 @@ import 'package:calendar_view/calendar_view.dart'; +import 'package:example/constants.dart'; import 'package:flutter/material.dart'; import '../pages/event_details_page.dart'; @@ -23,7 +24,10 @@ class DayViewWidget extends StatelessWidget { heightPerMinute: 3, timeLineBuilder: _timeLineBuilder, scrollPhysics: const BouncingScrollPhysics(), - eventArranger: SideEventArranger(maxWidth: 30), + eventArranger: SideEventArranger( + maxWidth: 30, + directionality: Directionality.of(context), + ), hourIndicatorSettings: HourIndicatorSettings( color: Theme.of(context).dividerColor, ), @@ -71,9 +75,10 @@ class DayViewWidget extends StatelessWidget { Positioned.fill( top: -8, right: 8, + left: 8, child: Text( "${date.hour}:${date.minute}", - textAlign: TextAlign.right, + textAlign: TextAlign.center, style: TextStyle( color: Colors.black.withAlpha(50), fontStyle: FontStyle.italic, @@ -92,9 +97,10 @@ class DayViewWidget extends StatelessWidget { Positioned.fill( top: -8, right: 8, + left: 8, child: Text( - "$hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}", - textAlign: TextAlign.right, + "${AppConstants.ltr} $hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}", + textAlign: TextAlign.center, ), ), ], diff --git a/example/lib/widgets/week_view_widget.dart b/example/lib/widgets/week_view_widget.dart index 4222c531..a0faa123 100644 --- a/example/lib/widgets/week_view_widget.dart +++ b/example/lib/widgets/week_view_widget.dart @@ -1,4 +1,5 @@ import 'package:calendar_view/calendar_view.dart'; +import 'package:example/constants.dart'; import 'package:flutter/material.dart'; import '../pages/event_details_page.dart'; @@ -14,14 +15,27 @@ class WeekViewWidget extends StatelessWidget { return WeekView( key: state, width: width, + headerStringBuilder: (DateTime date, {DateTime? secondaryDate}) => + _weekStringBuilder( + date, + secondaryDate: secondaryDate, + textDirection: Directionality.of(context), + ), showWeekends: true, showLiveTimeLineInAllDays: true, - eventArranger: SideEventArranger(maxWidth: 30), - timeLineWidth: 65, + eventArranger: SideEventArranger( + maxWidth: 30, + directionality: Directionality.of(context), + ), + timeLineWidth: 68, scrollPhysics: const BouncingScrollPhysics(), liveTimeIndicatorSettings: LiveTimeIndicatorSettings( color: Colors.redAccent, + timeBackgroundViewWidth: 68, + offset: 0, showTime: true, + showBullet: true, + showTimeBackgroundView: true, ), onTimestampTap: (date) { SnackBar snackBar = SnackBar( @@ -45,4 +59,19 @@ class WeekViewWidget extends StatelessWidget { }, ); } + + // TODO(Shubham): Include in readme to guide how to support RTL for string like below + String _weekStringBuilder(DateTime date, + {DateTime? secondaryDate, TextDirection? textDirection}) { + final dateString = "${date.day} / ${date.month} / ${date.year}"; + final secondaryDateString = secondaryDate != null + ? "${secondaryDate.day} / ${secondaryDate.month} / ${secondaryDate.year}" + : ""; + + if (textDirection == TextDirection.rtl) { + return "${AppConstants.ltr}${secondaryDateString} to ${dateString}"; + } else { + return "${AppConstants.ltr}${dateString} to ${secondaryDateString}"; + } + } } diff --git a/lib/src/components/_internal_components.dart b/lib/src/components/_internal_components.dart index a13bd432..e8c8009f 100644 --- a/lib/src/components/_internal_components.dart +++ b/lib/src/components/_internal_components.dart @@ -98,6 +98,12 @@ class _LiveTimeIndicatorState extends State { /// to set dy offset of live time indicator final startMinutes = widget.startHour * 60; + /// To support LTR & RTL we need to manage X position of point-1 to draw line + /// according to position of timeline add and subtract its width + final offsetX = Directionality.of(context) == TextDirection.ltr + ? widget.liveTimeIndicatorSettings.offset + widget.timeLineWidth + : widget.liveTimeIndicatorSettings.offset - widget.timeLineWidth; + /// Check if live time is not between startHour and endHour if it is then /// don't show live time indicator /// @@ -111,9 +117,10 @@ class _LiveTimeIndicatorState extends State { size: Size(widget.width, widget.liveTimeIndicatorSettings.height), painter: CurrentTimeLinePainter( color: widget.liveTimeIndicatorSettings.color, + textDirection: Directionality.of(context), height: widget.liveTimeIndicatorSettings.height, offset: Offset( - widget.timeLineWidth + widget.liveTimeIndicatorSettings.offset, + offsetX, (_currentTime.getTotalMinutes - startMinutes) * widget.heightPerMinute, ), diff --git a/lib/src/components/day_view_components.dart b/lib/src/components/day_view_components.dart index b8e28e9e..d16c9003 100644 --- a/lib/src/components/day_view_components.dart +++ b/lib/src/components/day_view_components.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import '../calendar_event_data.dart'; +import '../constants.dart'; import '../extensions.dart'; import '../typedefs.dart'; @@ -63,7 +64,6 @@ class RoundedEventTile extends StatelessWidget { borderRadius: borderRadius, ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ if (title.isNotEmpty) @@ -97,6 +97,7 @@ class RoundedEventTile extends StatelessWidget { Expanded( child: Text( "+${totalEvents - 1} more", + textAlign: TextAlign.center, style: (descriptionStyle ?? TextStyle( color: backgroundColor.accent.withAlpha(200), @@ -134,17 +135,19 @@ class DefaultTimeLineMark extends StatelessWidget { final timeString = (timeStringBuilder != null) ? timeStringBuilder!(date) : date.minute != 0 - ? "$hour:${date.minute}" - : "$hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}"; + ? "${Constants.ltr}$hour:${date.minute}" + : "${Constants.ltr}$hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}"; return Transform.translate( offset: Offset(0, -7.5), child: Padding( - padding: const EdgeInsets.only(right: 7.0), + padding: const EdgeInsets.only(right: 7.0, left: 7.0), child: Text( timeString, - textAlign: TextAlign.right, + textAlign: Directionality.of(context) == TextDirection.ltr + ? TextAlign.right + : TextAlign.left, style: markingStyle ?? - TextStyle( + const TextStyle( fontSize: 15.0, ), ), @@ -212,14 +215,19 @@ class FullDayEventView extends StatelessWidget { margin: const EdgeInsets.all(5.0), padding: const EdgeInsets.all(1.0), height: 24, - child: Text( - events[index].title, - style: titleStyle ?? - TextStyle( - fontSize: 16, - color: events[index].color.accent, - ), - maxLines: 1, + child: Align( + alignment: Directionality.of(context) == TextDirection.ltr + ? Alignment.centerLeft + : Alignment.centerRight, + child: Text( + events[index].title, + style: titleStyle ?? + TextStyle( + fontSize: 16, + color: events[index].color.accent, + ), + maxLines: 1, + ), ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), diff --git a/lib/src/constants.dart b/lib/src/constants.dart index 158379c8..f0204e6d 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -10,6 +10,7 @@ class Constants { static final Random _random = Random(); static final int _maxColor = 256; + static final ltr = '\u202A'; // Use this to force text direction LTR static const int hoursADay = 24; static const int minutesADay = 1440; diff --git a/lib/src/day_view/_internal_day_view_page.dart b/lib/src/day_view/_internal_day_view_page.dart index 28424c74..c5a8b8d9 100644 --- a/lib/src/day_view/_internal_day_view_page.dart +++ b/lib/src/day_view/_internal_day_view_page.dart @@ -269,6 +269,7 @@ class _InternalDayViewPageState widget.halfHourIndicatorSettings.dashSpaceWidth, startHour: widget.startHour, endHour: widget.endHour, + textDirection: Directionality.of(context), ), ), if (widget.showQuarterHours) @@ -287,6 +288,8 @@ class _InternalDayViewPageState widget.quarterHourIndicatorSettings.dashWidth, dashSpaceWidth: widget .quarterHourIndicatorSettings.dashSpaceWidth, + textDirection: Directionality.of(context), + timelineWidth: widget.timeLineWidth, ), ), widget.dayDetectorBuilder( @@ -297,7 +300,9 @@ class _InternalDayViewPageState minuteSlotSize: widget.minuteSlotSize, ), Align( - alignment: Alignment.centerRight, + alignment: Directionality.of(context) == TextDirection.ltr + ? Alignment.centerRight + : Alignment.centerLeft, child: EventGenerator( height: widget.height, date: widget.date, diff --git a/lib/src/day_view/day_view.dart b/lib/src/day_view/day_view.dart index c290fd19..82d019c6 100644 --- a/lib/src/day_view/day_view.dart +++ b/lib/src/day_view/day_view.dart @@ -566,6 +566,7 @@ class DayViewState extends State> { _halfHourIndicatorSettings = widget.halfHourIndicatorSettings ?? HourIndicatorSettings( height: widget.heightPerMinute, + lineStyle: LineStyle.dashed, color: Constants.defaultBorderColor, offset: 5, ); @@ -731,10 +732,13 @@ class DayViewState extends State> { int startHour, int endHour, ) { + final directionality = Directionality.of(context); return HourLinePainter( lineColor: lineColor, lineHeight: lineHeight, - offset: offset, + timelineWidth: widget.timeLineWidth, + textDirection: directionality, + offset: directionality == TextDirection.ltr ? offset : 0, minuteHeight: minuteHeight, verticalLineOffset: verticalLineOffset, showVerticalLine: showVerticalLine, diff --git a/lib/src/event_arrangers/side_event_arranger.dart b/lib/src/event_arrangers/side_event_arranger.dart index 5c0283c9..473bc943 100644 --- a/lib/src/event_arrangers/side_event_arranger.dart +++ b/lib/src/event_arrangers/side_event_arranger.dart @@ -10,6 +10,7 @@ class SideEventArranger extends EventArranger { const SideEventArranger({ this.maxWidth, this.includeEdges = false, + this.directionality = TextDirection.ltr, }); /// Decides whether events that are overlapping on edge @@ -26,6 +27,9 @@ class SideEventArranger extends EventArranger { /// If max width is not specified, slots will expand to fill the cell. final double? maxWidth; + /// Defines the directionality LRT/RTL + final TextDirection directionality; + /// {@macro event_arranger_arrange_method_doc} /// /// Make sure that all the events that are passed in [events], must be in @@ -130,9 +134,11 @@ class SideEventArranger extends EventArranger { final top = (startTime.getTotalMinutes - (startHour * 60)) * heightPerMinute; + final isLtr = directionality == TextDirection.ltr; + return OrganizedCalendarEventData( - left: offset, - right: totalWidth - (offset + slotWidth), + left: isLtr ? offset : totalWidth - (offset + slotWidth), + right: isLtr ? totalWidth - (offset + slotWidth) : offset, top: top, bottom: bottom, startDuration: startTime, diff --git a/lib/src/painters.dart b/lib/src/painters.dart index 4498bfb3..1911d89b 100644 --- a/lib/src/painters.dart +++ b/lib/src/painters.dart @@ -47,6 +47,12 @@ class HourLinePainter extends CustomPainter { /// This field will be used to set end hour for day and week view final int endHour; + /// Defines the width of timeline + final double? timelineWidth; + + /// Defines directionality + final TextDirection textDirection; + /// Paints 24 hour lines. HourLinePainter({ required this.lineColor, @@ -56,45 +62,68 @@ class HourLinePainter extends CustomPainter { required this.showVerticalLine, required this.startHour, required this.emulateVerticalOffsetBy, + this.timelineWidth, this.endHour = Constants.hoursADay, this.verticalLineOffset = 10, this.lineStyle = LineStyle.solid, this.dashWidth = 4, this.dashSpaceWidth = 4, + this.textDirection = TextDirection.ltr, }); + bool get isLtr => textDirection == TextDirection.ltr; + @override void paint(Canvas canvas, Size size) { final dx = offset + emulateVerticalOffsetBy; final paint = Paint() ..color = lineColor ..strokeWidth = lineHeight; + // X point of Point P2 + final endXPoint = size.width - (isLtr ? 0 : timelineWidth ?? 0); for (var i = startHour + 1; i < endHour; i++) { final dy = (i - startHour) * minuteHeight * 60; if (lineStyle == LineStyle.dashed) { var startX = dx; - while (startX < size.width) { + final width = isLtr ? size.width : size.width - (timelineWidth ?? 0); + + while (startX < width) { canvas.drawLine( - Offset(startX, dy), Offset(startX + dashWidth, dy), paint); + Offset(startX, dy), + Offset(startX + dashWidth, dy), + paint, + ); startX += dashWidth + dashSpaceWidth; } } else { - canvas.drawLine(Offset(dx, dy), Offset(size.width, dy), paint); + final startX = isLtr ? dx : dx - (timelineWidth ?? 0); + canvas.drawLine(Offset(startX, dy), Offset(endXPoint, dy), paint); } } if (showVerticalLine) { + final ltrOffset = offset + verticalLineOffset; + final rtlOffset = size.width - verticalLineOffset - (timelineWidth ?? 0); + if (lineStyle == LineStyle.dashed) { + final xPoint = isLtr ? ltrOffset : rtlOffset; var startY = 0.0; + while (startY < size.height) { - canvas.drawLine(Offset(offset + verticalLineOffset, startY), - Offset(offset + verticalLineOffset, startY + dashWidth), paint); + canvas.drawLine( + Offset(xPoint, startY), + Offset(xPoint, startY + dashWidth), + paint, + ); startY += dashWidth + dashSpaceWidth; } } else { - canvas.drawLine(Offset(offset + verticalLineOffset, 0), - Offset(offset + verticalLineOffset, size.height), paint); + canvas.drawLine( + Offset(isLtr ? ltrOffset : rtlOffset, 0), + Offset(isLtr ? ltrOffset : rtlOffset, size.height), + paint, + ); } } } @@ -138,6 +167,9 @@ class HalfHourLinePainter extends CustomPainter { /// This field will be used to set end hour for day and week view final int endHour; + /// Defines directionality + final TextDirection textDirection; + /// Paint half hour lines HalfHourLinePainter({ required this.lineColor, @@ -149,8 +181,11 @@ class HalfHourLinePainter extends CustomPainter { this.dashWidth = 4, this.dashSpaceWidth = 4, this.endHour = Constants.hoursADay, + this.textDirection = TextDirection.ltr, }); + bool get isLtr => textDirection == TextDirection.ltr; + @override void paint(Canvas canvas, Size size) { final paint = Paint() @@ -159,15 +194,21 @@ class HalfHourLinePainter extends CustomPainter { for (var i = startHour; i < endHour; i++) { final dy = (i - startHour) * minuteHeight * 60 + (minuteHeight * 30); + final width = isLtr ? size.width : size.width - offset; if (lineStyle == LineStyle.dashed) { - var startX = offset; - while (startX < size.width) { + var startX = isLtr ? offset : 0.0; + while (startX < width) { canvas.drawLine( - Offset(startX, dy), Offset(startX + dashWidth, dy), paint); + Offset(startX, dy), + Offset(startX + dashWidth, dy), + paint, + ); startX += dashWidth + dashSpaceWidth; } } else { - canvas.drawLine(Offset(offset, dy), Offset(size.width, dy), paint); + final startX = isLtr ? offset : 0.0; + final endX = isLtr ? width : size.width - offset; + canvas.drawLine(Offset(startX, dy), Offset(endX, dy), paint); } } } @@ -205,6 +246,12 @@ class QuarterHourLinePainter extends CustomPainter { /// Line dash space width when using the [LineStyle.dashed] style final double dashSpaceWidth; + /// Defines the width of timeline + final double? timelineWidth; + + /// Defines directionality + final TextDirection textDirection; + /// Paint quarter hour lines QuarterHourLinePainter({ required this.lineColor, @@ -212,10 +259,14 @@ class QuarterHourLinePainter extends CustomPainter { required this.offset, required this.minuteHeight, required this.lineStyle, + this.timelineWidth, this.dashWidth = 4, this.dashSpaceWidth = 4, + this.textDirection = TextDirection.ltr, }); + bool get isLtr => textDirection == TextDirection.ltr; + @override void paint(Canvas canvas, Size size) { final paint = Paint() @@ -225,21 +276,39 @@ class QuarterHourLinePainter extends CustomPainter { for (var i = 0; i < Constants.hoursADay; i++) { final dy1 = i * minuteHeight * 60 + (minuteHeight * 15); final dy2 = i * minuteHeight * 60 + (minuteHeight * 45); + final endX = isLtr ? size.width : size.width - offset; if (lineStyle == LineStyle.dashed) { var startX = offset; - while (startX < size.width) { + + while (startX < endX) { canvas.drawLine( - Offset(startX, dy1), Offset(startX + dashWidth, dy1), paint); + Offset(startX, dy1), + Offset(startX + dashWidth, dy1), + paint, + ); startX += dashWidth + dashSpaceWidth; canvas.drawLine( - Offset(startX, dy2), Offset(startX + dashWidth, dy2), paint); + Offset(startX, dy2), + Offset(startX + dashWidth, dy2), + paint, + ); startX += dashWidth + dashSpaceWidth; } } else { - canvas.drawLine(Offset(offset, dy1), Offset(size.width, dy1), paint); - canvas.drawLine(Offset(offset, dy2), Offset(size.width, dy2), paint); + final startXPoint = isLtr ? offset : 0.0; + canvas + ..drawLine( + Offset(startXPoint, dy1), + Offset(endX, dy1), + paint, + ) + ..drawLine( + Offset(startXPoint, dy2), + Offset(endX, dy2), + paint, + ); } } } @@ -283,6 +352,11 @@ class CurrentTimeLinePainter extends CustomPainter { /// Width of time backgroud view. final double timeBackgroundViewWidth; + /// Defines directionality + final TextDirection textDirection; + + bool get isLtr => textDirection == TextDirection.ltr; + /// Paints a single horizontal line at [offset]. CurrentTimeLinePainter({ required this.showBullet, @@ -294,33 +368,46 @@ class CurrentTimeLinePainter extends CustomPainter { required this.showTime, required this.showTimeBackgroundView, required this.timeBackgroundViewWidth, + this.textDirection = TextDirection.ltr, }); @override void paint(Canvas canvas, Size size) { + final startXPoint = isLtr + ? offset.dx - (showBullet ? 0 : 8) + : offset.dx - (showBullet ? 8 : 0); + final endXPoint = size.width - (isLtr ? 0 : timeBackgroundViewWidth); canvas.drawLine( - Offset(offset.dx - (showBullet ? 0 : 8), offset.dy), - Offset(size.width, offset.dy), + Offset(startXPoint, offset.dy), + Offset(endXPoint, offset.dy), Paint() ..color = color ..strokeWidth = height, ); if (showBullet) { + final xPoint = isLtr ? offset.dx : offset.dx + size.width; canvas.drawCircle( - Offset(offset.dx, offset.dy), bulletRadius, Paint()..color = color); + Offset(xPoint, offset.dy), + bulletRadius, + Paint()..color = color, + ); } if (showTimeBackgroundView) { + final dx = isLtr + ? offset.dx - timeBackgroundViewWidth - 4 + : offset.dx + size.width; + canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH( - max(3, offset.dx - 68), + max(3, dx), offset.dy - 11, timeBackgroundViewWidth, 24, ), - Radius.circular(12), + const Radius.circular(12), ), Paint() ..color = color @@ -335,13 +422,19 @@ class CurrentTimeLinePainter extends CustomPainter { text: TextSpan( text: timeString, style: TextStyle( - fontSize: 12.0, + fontSize: 12, color: showTimeBackgroundView ? Colors.white : color, ), ), ) ..layout() - ..paint(canvas, Offset(offset.dx - 62, offset.dy - 6)); + ..paint( + canvas, + Offset( + isLtr ? 8 : offset.dx + size.width + 6, + offset.dy - 6, + ), + ); } } diff --git a/lib/src/week_view/_internal_week_view_page.dart b/lib/src/week_view/_internal_week_view_page.dart index 85d36b54..c965b328 100644 --- a/lib/src/week_view/_internal_week_view_page.dart +++ b/lib/src/week_view/_internal_week_view_page.dart @@ -251,6 +251,8 @@ class _InternalWeekViewPageState @override Widget build(BuildContext context) { final filteredDates = _filteredDate(); + final isLtr = Directionality.of(context) == TextDirection.ltr; + return Container( height: widget.height + widget.weekTitleHeight, width: widget.width, @@ -311,6 +313,7 @@ class _InternalWeekViewPageState vertical: 2, horizontal: 1, ), + // TODO(Shubham): Add direction child: Text( widget.fullDayHeaderTitle, textAlign: @@ -353,23 +356,33 @@ class _InternalWeekViewPageState width: widget.width, child: Stack( children: [ - CustomPaint( - size: Size(widget.width, widget.height), - painter: HourLinePainter( - lineColor: widget.hourIndicatorSettings.color, - lineHeight: widget.hourIndicatorSettings.height, - offset: widget.timeLineWidth + - widget.hourIndicatorSettings.offset, - minuteHeight: widget.heightPerMinute, - verticalLineOffset: widget.verticalLineOffset, - showVerticalLine: widget.showVerticalLine, - lineStyle: widget.hourIndicatorSettings.lineStyle, - dashWidth: widget.hourIndicatorSettings.dashWidth, - dashSpaceWidth: - widget.hourIndicatorSettings.dashSpaceWidth, - emulateVerticalOffsetBy: widget.emulateVerticalOffsetBy, - startHour: widget.startHour, - endHour: widget.endHour, + // TODO(Shubham): Width of horizontal paint line + Positioned( + left: isLtr ? widget.timeLineWidth : 0, + // TODO(Shubham): Check padding of 6 from somewhere + right: isLtr ? 0 : widget.timeLineWidth + 6, + child: CustomPaint( + size: Size( + widget.width, + widget.height, + ), + painter: HourLinePainter( + lineColor: widget.hourIndicatorSettings.color, + lineHeight: widget.hourIndicatorSettings.height, + offset: + isLtr ? widget.hourIndicatorSettings.offset : 0, + minuteHeight: widget.heightPerMinute, + verticalLineOffset: widget.verticalLineOffset, + showVerticalLine: widget.showVerticalLine, + lineStyle: widget.hourIndicatorSettings.lineStyle, + dashWidth: widget.hourIndicatorSettings.dashWidth, + dashSpaceWidth: + widget.hourIndicatorSettings.dashSpaceWidth, + emulateVerticalOffsetBy: + widget.emulateVerticalOffsetBy, + startHour: widget.startHour, + endHour: widget.endHour, + ), ), ), if (widget.showHalfHours) @@ -405,10 +418,13 @@ class _InternalWeekViewPageState widget.quarterHourIndicatorSettings.dashWidth, dashSpaceWidth: widget .quarterHourIndicatorSettings.dashSpaceWidth, + textDirection: Directionality.of(context), ), ), Align( - alignment: Alignment.centerRight, + alignment: Directionality.of(context) == TextDirection.ltr + ? Alignment.centerRight + : Alignment.centerLeft, child: SizedBox( width: widget.weekTitleWidth * filteredDates.length, height: widget.height, @@ -486,7 +502,7 @@ class _InternalWeekViewPageState LiveTimeIndicator( liveTimeIndicatorSettings: widget.liveTimeIndicatorSettings, - width: widget.width, + width: widget.width - 8, height: widget.height, heightPerMinute: widget.heightPerMinute, timeLineWidth: widget.timeLineWidth, diff --git a/lib/src/week_view/week_view.dart b/lib/src/week_view/week_view.dart index b91efa13..7b3d202e 100644 --- a/lib/src/week_view/week_view.dart +++ b/lib/src/week_view/week_view.dart @@ -886,6 +886,7 @@ class WeekViewState extends State> { emulateVerticalOffsetBy: emulateVerticalOffsetBy, startHour: startHour, endHour: endHour, + textDirection: Directionality.of(context), ); }