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..d4fa4fdf 100644 --- a/README.md +++ b/README.md @@ -353,6 +353,28 @@ WeekView( ); ``` +### 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(), + ), + ), +); +``` + Above code will create `WeekView` with only five days, from monday to friday. ## Main Contributors diff --git a/example/lib/constants.dart b/example/lib/constants.dart index 21da78d8..d359d9b0 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 keep text direction LTR static OutlineInputBorder inputBorder = OutlineInputBorder( borderRadius: BorderRadius.circular(7), diff --git a/example/lib/pages/day_view_page.dart b/example/lib/pages/day_view_page.dart index 1a80aba2..506f7aa6 100644 --- a/example/lib/pages/day_view_page.dart +++ b/example/lib/pages/day_view_page.dart @@ -17,17 +17,20 @@ class DayViewPageDemo extends StatefulWidget { class _DayViewPageDemoState extends State { @override Widget build(BuildContext context) { - return ResponsiveWidget( - webWidget: WebHomePage( - selectedView: CalendarView.day, - ), - mobileWidget: Scaffold( - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - elevation: 8, - onPressed: () => context.pushRoute(CreateEventPage()), + return Directionality( + textDirection: TextDirection.rtl, + child: ResponsiveWidget( + webWidget: WebHomePage( + selectedView: CalendarView.day, + ), + mobileWidget: Scaffold( + floatingActionButton: FloatingActionButton( + child: Icon(Icons.add), + elevation: 8, + onPressed: () => context.pushRoute(CreateEventPage()), + ), + body: DayViewWidget(), ), - body: DayViewWidget(), ), ); } diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart index 85d8f05d..06f09a7b 100644 --- a/example/lib/pages/home_page.dart +++ b/example/lib/pages/home_page.dart @@ -9,9 +9,12 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { - return ResponsiveWidget( - mobileWidget: MobileHomePage(), - webWidget: WebHomePage(), + return Directionality( + textDirection: TextDirection.rtl, + child: ResponsiveWidget( + mobileWidget: MobileHomePage(), + webWidget: WebHomePage(), + ), ); } } diff --git a/example/lib/pages/mobile/mobile_home_page.dart b/example/lib/pages/mobile/mobile_home_page.dart index 640dde95..ebc033b9 100644 --- a/example/lib/pages/mobile/mobile_home_page.dart +++ b/example/lib/pages/mobile/mobile_home_page.dart @@ -8,6 +8,8 @@ import '../week_view_page.dart'; class MobileHomePage extends StatelessWidget { @override Widget build(BuildContext context) { + debugPrint( + 'Directionality mobile before pushed: ${Directionality.of(context)}'); return Scaffold( appBar: AppBar( title: Text("Flutter Calendar Page"), diff --git a/example/lib/pages/month_view_page.dart b/example/lib/pages/month_view_page.dart index 9bd798c2..d35f46a7 100644 --- a/example/lib/pages/month_view_page.dart +++ b/example/lib/pages/month_view_page.dart @@ -19,17 +19,20 @@ class MonthViewPageDemo extends StatefulWidget { class _MonthViewPageDemoState extends State { @override Widget build(BuildContext context) { - return ResponsiveWidget( - webWidget: WebHomePage( - selectedView: CalendarView.month, - ), - mobileWidget: Scaffold( - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - elevation: 8, - onPressed: () => context.pushRoute(CreateEventPage()), + return Directionality( + textDirection: TextDirection.rtl, + child: ResponsiveWidget( + webWidget: WebHomePage( + selectedView: CalendarView.month, + ), + mobileWidget: Scaffold( + floatingActionButton: FloatingActionButton( + child: Icon(Icons.add), + elevation: 8, + onPressed: () => context.pushRoute(CreateEventPage()), + ), + body: MonthViewWidget(), ), - body: MonthViewWidget(), ), ); } diff --git a/example/lib/pages/week_view_page.dart b/example/lib/pages/week_view_page.dart index 3a183f5e..047f83ab 100644 --- a/example/lib/pages/week_view_page.dart +++ b/example/lib/pages/week_view_page.dart @@ -17,17 +17,20 @@ class WeekViewDemo extends StatefulWidget { class _WeekViewDemoState extends State { @override Widget build(BuildContext context) { - return ResponsiveWidget( - webWidget: WebHomePage( - selectedView: CalendarView.week, - ), - mobileWidget: Scaffold( - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - elevation: 8, - onPressed: () => context.pushRoute(CreateEventPage()), + return 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(), ), - body: WeekViewWidget(), ), ); } diff --git a/example/lib/widgets/add_event_form.dart b/example/lib/widgets/add_event_form.dart index dbdf31c9..cc863a97 100644 --- a/example/lib/widgets/add_event_form.dart +++ b/example/lib/widgets/add_event_form.dart @@ -256,7 +256,7 @@ class _AddOrEditEventFormState extends State { ), ), Align( - alignment: Alignment.centerLeft, + alignment: Alignment.centerRight, child: Text( 'Repeat', style: TextStyle( @@ -519,7 +519,7 @@ class _AddOrEditEventFormState extends State { Row( children: [ Text( - "Event Color: ", + "${AppConstants.ltr}Event Color: ", style: TextStyle( color: AppColors.black, fontSize: 17, 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..78cd93b1 100644 --- a/example/lib/widgets/week_view_widget.dart +++ b/example/lib/widgets/week_view_widget.dart @@ -14,14 +14,24 @@ 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), + eventArranger: SideEventArranger( + maxWidth: 30, + directionality: Directionality.of(context), + ), timeLineWidth: 65, scrollPhysics: const BouncingScrollPhysics(), liveTimeIndicatorSettings: LiveTimeIndicatorSettings( color: Colors.redAccent, showTime: true, + showTimeBackgroundView: true, ), onTimestampTap: (date) { SnackBar snackBar = SnackBar( @@ -45,4 +55,24 @@ 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}" + : ""; + // Unicode character for Left-to-Right Embedding + // (to enforce LTR in the string) + const ltr = '\u202A'; + // Unicode character for Pop Directional Formatting + // (to close the directional formatting) + const pop = '\u202C'; + if (textDirection == TextDirection.rtl) { + return "$ltr${secondaryDateString} to ${dateString}$pop"; + } else { + return "$ltr${dateString} to ${secondaryDateString}$pop"; + } + } } diff --git a/lib/src/components/_internal_components.dart b/lib/src/components/_internal_components.dart index a13bd432..0381e9c4 100644 --- a/lib/src/components/_internal_components.dart +++ b/lib/src/components/_internal_components.dart @@ -111,9 +111,11 @@ 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, + width: widget.width - widget.timeLineWidth - 12, offset: Offset( - widget.timeLineWidth + widget.liveTimeIndicatorSettings.offset, + widget.liveTimeIndicatorSettings.offset, (_currentTime.getTotalMinutes - startMinutes) * widget.heightPerMinute, ), @@ -230,6 +232,7 @@ class _TimeLineState extends State { ), child: Stack( children: [ + // TODO(Shubham): Update padding for RTL for (int i = widget.startHour + 1; i < widget.endHour; i++) _timelinePositioned( topPosition: widget.hourHeight * (i - widget.startHour) - diff --git a/lib/src/components/day_view_components.dart b/lib/src/components/day_view_components.dart index b8e28e9e..4b175486 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'; @@ -120,12 +121,15 @@ class DefaultTimeLineMark extends StatelessWidget { /// Text style for time string. final TextStyle? markingStyle; + final TextDirection? textDirection; + /// Time marker for timeline used in week and day view. const DefaultTimeLineMark({ Key? key, required this.date, this.markingStyle, this.timeStringBuilder, + this.textDirection, }) : super(key: key); @override @@ -134,15 +138,18 @@ 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, + // textDirection: Directionality.of(context), + textAlign: Directionality.of(context) == TextDirection.ltr + ? TextAlign.right + : TextAlign.left, style: markingStyle ?? TextStyle( fontSize: 15.0, @@ -212,14 +219,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..0070bd37 100644 --- a/lib/src/day_view/_internal_day_view_page.dart +++ b/lib/src/day_view/_internal_day_view_page.dart @@ -214,6 +214,15 @@ class _InternalDayViewPageState @override Widget build(BuildContext context) { final fullDayEventList = widget.controller.getFullDayEvent(widget.date); + final hourLineOffset = (Directionality.of(context) == TextDirection.ltr + ? widget.timeLineWidth + : 0) + + widget.hourIndicatorSettings.offset; + final halfHourOffset = (Directionality.of(context) == TextDirection.ltr + ? widget.timeLineWidth + : 0) + + widget.halfHourIndicatorSettings.offset; + return Container( height: widget.height, width: widget.width, @@ -269,6 +278,8 @@ class _InternalDayViewPageState widget.halfHourIndicatorSettings.dashSpaceWidth, startHour: widget.startHour, endHour: widget.endHour, + textDirection: Directionality.of(context), + timelineWidth: widget.timeLineWidth, ), ), if (widget.showQuarterHours) @@ -296,8 +307,11 @@ class _InternalDayViewPageState date: widget.date, minuteSlotSize: widget.minuteSlotSize, ), + // TODO(Shubham): Update for directionality 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..2a51ed4e 100644 --- a/lib/src/day_view/day_view.dart +++ b/lib/src/day_view/day_view.dart @@ -731,10 +731,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..ebaea905 100644 --- a/lib/src/painters.dart +++ b/lib/src/painters.dart @@ -46,6 +46,8 @@ class HourLinePainter extends CustomPainter { /// This field will be used to set end hour for day and week view final int endHour; + final double? timelineWidth; + final TextDirection textDirection; /// Paints 24 hour lines. HourLinePainter({ @@ -56,11 +58,13 @@ 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, }); @override @@ -69,9 +73,13 @@ class HourLinePainter extends CustomPainter { final paint = Paint() ..color = lineColor ..strokeWidth = lineHeight; + final isLtr = textDirection == TextDirection.ltr; + // 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; + // TODO(Shubham): Check for dashed line as well for week view as well if (lineStyle == LineStyle.dashed) { var startX = dx; while (startX < size.width) { @@ -80,11 +88,13 @@ class HourLinePainter extends CustomPainter { startX += dashWidth + dashSpaceWidth; } } else { - canvas.drawLine(Offset(dx, dy), Offset(size.width, dy), paint); + canvas.drawLine(Offset(dx, dy), Offset(endXPoint, dy), paint); } } + // TODO(Shubham): Draws vertical line at start disable it to remove leading vertical line if (showVerticalLine) { + // TODO(Shubham): Check for dashed line as well for week view as well if (lineStyle == LineStyle.dashed) { var startY = 0.0; while (startY < size.height) { @@ -93,8 +103,14 @@ class HourLinePainter extends CustomPainter { startY += dashWidth + dashSpaceWidth; } } else { - canvas.drawLine(Offset(offset + verticalLineOffset, 0), - Offset(offset + verticalLineOffset, size.height), paint); + final ltrWidth = offset + verticalLineOffset; + final rtlWidth = + offset + verticalLineOffset + size.width - (timelineWidth ?? 0); + final isLtr = textDirection == TextDirection.ltr; + + // TODO(Shubham): Move vertical line to right for RTL + canvas.drawLine(Offset(isLtr ? ltrWidth : rtlWidth, 0), + Offset(isLtr ? ltrWidth : rtlWidth, size.height), paint); } } } @@ -137,6 +153,8 @@ class HalfHourLinePainter extends CustomPainter { /// This field will be used to set end hour for day and week view final int endHour; + final TextDirection textDirection; + final double? timelineWidth; /// Paint half hour lines HalfHourLinePainter({ @@ -149,6 +167,8 @@ class HalfHourLinePainter extends CustomPainter { this.dashWidth = 4, this.dashSpaceWidth = 4, this.endHour = Constants.hoursADay, + this.textDirection = TextDirection.ltr, + this.timelineWidth, }); @override @@ -156,17 +176,22 @@ class HalfHourLinePainter extends CustomPainter { final paint = Paint() ..color = lineColor ..strokeWidth = lineHeight; + final isLtr = textDirection == TextDirection.ltr; + // X point of Point P2 + final endXPoint = size.width - (isLtr ? 0 : timelineWidth ?? 0); for (var i = startHour; i < endHour; i++) { final dy = (i - startHour) * minuteHeight * 60 + (minuteHeight * 30); if (lineStyle == LineStyle.dashed) { - var startX = offset; - while (startX < size.width) { + var startX = isLtr ? offset : 0.0; + final width = isLtr ? size.width : size.width - (timelineWidth ?? 0); + while (startX < width) { canvas.drawLine( Offset(startX, dy), Offset(startX + dashWidth, dy), paint); startX += dashWidth + dashSpaceWidth; } } else { + // TODO(Shubham): Handle non-dashed line canvas.drawLine(Offset(offset, dy), Offset(size.width, dy), paint); } } @@ -261,10 +286,12 @@ class CurrentTimeLinePainter extends CustomPainter { /// Height of time indicator. final double height; + final double width; /// offset of time indicator. final Offset offset; + // TODO(Shubham): Update docs /// Flag to show bullet at left side or not. final bool showBullet; @@ -282,6 +309,7 @@ class CurrentTimeLinePainter extends CustomPainter { /// Width of time backgroud view. final double timeBackgroundViewWidth; + final TextDirection textDirection; /// Paints a single horizontal line at [offset]. CurrentTimeLinePainter({ @@ -294,28 +322,43 @@ class CurrentTimeLinePainter extends CustomPainter { required this.showTime, required this.showTimeBackgroundView, required this.timeBackgroundViewWidth, + required this.width, + this.textDirection = TextDirection.ltr, }); @override void paint(Canvas canvas, Size size) { + // TODO(Shubham): Check size.width is line width or not canvas.drawLine( Offset(offset.dx - (showBullet ? 0 : 8), offset.dy), - Offset(size.width, offset.dy), + Offset( + textDirection == TextDirection.ltr ? size.width : width, offset.dy), Paint() ..color = color ..strokeWidth = height, ); if (showBullet) { - canvas.drawCircle( - Offset(offset.dx, offset.dy), bulletRadius, Paint()..color = color); + if (textDirection == TextDirection.ltr) { + canvas.drawCircle( + Offset(offset.dx + timeBackgroundViewWidth, offset.dy), + bulletRadius, + Paint()..color = color); + } else { + canvas.drawCircle(Offset(offset.dx + width, offset.dy), bulletRadius, + Paint()..color = color); + } } if (showTimeBackgroundView) { + final dx = textDirection == TextDirection.ltr + ? offset.dx - 4 + : offset.dx + width + 4; + canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH( - max(3, offset.dx - 68), + max(3, dx), offset.dy - 11, timeBackgroundViewWidth, 24, @@ -341,7 +384,13 @@ class CurrentTimeLinePainter extends CustomPainter { ), ) ..layout() - ..paint(canvas, Offset(offset.dx - 62, offset.dy - 6)); + ..paint( + canvas, + Offset( + textDirection == TextDirection.ltr + ? offset.dx + : offset.dx + width + 7, + 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..18ea0050 100644 --- a/lib/src/week_view/_internal_week_view_page.dart +++ b/lib/src/week_view/_internal_week_view_page.dart @@ -166,6 +166,8 @@ class InternalWeekViewPage extends StatefulWidget { /// This method will be called when user taps on timestamp in timeline. final TimestampCallback? onTimestampTap; + final TextDirection textDirection; + /// A single page for week view. const InternalWeekViewPage({ Key? key, @@ -216,6 +218,7 @@ class InternalWeekViewPage extends StatefulWidget { required this.weekViewScrollController, this.lastScrollOffset = 0.0, this.keepScrollOffset = false, + this.textDirection = TextDirection.ltr, }) : super(key: key); @override @@ -251,6 +254,7 @@ class _InternalWeekViewPageState @override Widget build(BuildContext context) { final filteredDates = _filteredDate(); + final textDirection = Directionality.of(context); return Container( height: widget.height + widget.weekTitleHeight, width: widget.width, @@ -311,6 +315,7 @@ class _InternalWeekViewPageState vertical: 2, horizontal: 1, ), + // TODO(Shubham): Add direction child: Text( widget.fullDayHeaderTitle, textAlign: @@ -353,25 +358,50 @@ 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 + if (true) + Positioned( + left: Directionality.of(context) == TextDirection.ltr + ? widget.timeLineWidth + : 0, + // TODO(Shubham): Check padding of 6 from somewhere + right: Directionality.of(context) == TextDirection.ltr + ? 0 + : widget.timeLineWidth + 6, + child: CustomPaint( + size: Size( + widget.width, + widget.height, + ), + painter: HourLinePainter( + lineColor: widget.hourIndicatorSettings.color, + lineHeight: widget.hourIndicatorSettings.height, + offset: 0, + // TODO(Shubham): Update offset + // offset: + // Directionality.of(context) == TextDirection.ltr + // ? widget.timeLineWidth + + // widget.hourIndicatorSettings.offset + // : 0, + // offset: + // Directionality.of(context) == TextDirection.ltr + // ? widget.timeLineWidth + + // widget.hourIndicatorSettings.offset + // : widget.width - widget.timeLineWidth, + 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) CustomPaint( size: Size(widget.width, widget.height), @@ -408,7 +438,9 @@ class _InternalWeekViewPageState ), ), Align( - alignment: Alignment.centerRight, + alignment: Directionality.of(context) == TextDirection.ltr + ? Alignment.centerRight + : Alignment.centerLeft, child: SizedBox( width: widget.weekTitleWidth * filteredDates.length, height: widget.height, @@ -420,12 +452,28 @@ class _InternalWeekViewPageState decoration: widget.showVerticalLine ? BoxDecoration( border: Border( - right: BorderSide( - color: widget - .hourIndicatorSettings.color, - width: widget - .hourIndicatorSettings.height, - ), + left: widget.textDirection == + TextDirection.ltr + ? BorderSide.none + : BorderSide( + color: widget + .hourIndicatorSettings + .color, + width: widget + .hourIndicatorSettings + .height, + ), + right: widget.textDirection == + TextDirection.ltr + ? BorderSide( + color: widget + .hourIndicatorSettings + .color, + width: widget + .hourIndicatorSettings + .height, + ) + : BorderSide.none, ), ) : null, @@ -486,7 +534,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..cb5ba806 100644 --- a/lib/src/week_view/week_view.dart +++ b/lib/src/week_view/week_view.dart @@ -250,6 +250,8 @@ class WeekView extends StatefulWidget { /// Flag to keep scrollOffset of pages on page change final bool keepScrollOffset; + final TextDirection textDirection; + /// Main widget for week view. const WeekView({ Key? key, @@ -309,6 +311,7 @@ class WeekView extends StatefulWidget { this.fullDayHeaderTitle = '', this.fullDayHeaderTextConfig, this.keepScrollOffset = false, + this.textDirection = TextDirection.ltr, this.onTimestampTap, }) : assert(!(onHeaderTitleTap != null && weekPageHeaderBuilder != null), "can't use [onHeaderTitleTap] & [weekPageHeaderBuilder] simultaneously"), @@ -492,6 +495,7 @@ class WeekViewState extends State> { @override Widget build(BuildContext context) { + // TODO(Shubham): Check directionality for quarter and half hours as well return SafeAreaWrapper( option: widget.safeAreaOption, child: LayoutBuilder(builder: (context, constraint) { @@ -533,6 +537,7 @@ class WeekViewState extends State> { _hourHeight.toString() + dates[0].toString()), height: _height, width: _width, + textDirection: widget.textDirection, weekTitleWidth: _weekTitleWidth, weekTitleHeight: widget.weekTitleHeight, weekDayBuilder: _weekDayBuilder, @@ -807,6 +812,7 @@ class WeekViewState extends State> { Widget _defaultTimeLineBuilder(DateTime date) => DefaultTimeLineMark( date: date, timeStringBuilder: widget.timeLineStringBuilder, + textDirection: widget.textDirection, ); /// Default timeline builder. This builder will be used if