Skip to content

Commit 5c30cc0

Browse files
feat: Fixes issue #412: ✨ Add support for RTL directionality
1 parent 469cc29 commit 5c30cc0

16 files changed

+284
-84
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# [1.4.1 - Unreleased]
22

33
- Adds clear method to `EventController`.
4+
- Adds support of directionality. [#412](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/issues/412)
45

56
# [1.4.0 - 7 Jan 2025](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/tree/1.4.0)
67

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,30 @@ WeekView(
352352
],
353353
);
354354
```
355-
356355
Above code will create `WeekView` with only five days, from monday to friday.
357356

357+
### Support for RTL
358+
Wrap your widget with `Directionality` widget and use `textDirection` to give RTL or LTR direction.
359+
360+
```dart
361+
Directionality(
362+
textDirection: TextDirection.rtl,
363+
child: ResponsiveWidget(
364+
webWidget: WebHomePage(
365+
selectedView: CalendarView.week,
366+
),
367+
mobileWidget: Scaffold(
368+
floatingActionButton: FloatingActionButton(
369+
child: Icon(Icons.add),
370+
elevation: 8,
371+
onPressed: () => context.pushRoute(CreateEventPage()),
372+
),
373+
body: WeekViewWidget(),
374+
),
375+
),
376+
);
377+
```
378+
358379
## Main Contributors
359380

360381
<table>

example/lib/constants.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class AppConstants {
66
AppConstants._();
77

88
static final List<String> weekTitles = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
9+
static final ltr = '\u202A'; // Use this to force text direction to LTR
910

1011
static OutlineInputBorder inputBorder = OutlineInputBorder(
1112
borderRadius: BorderRadius.circular(7),

example/lib/widgets/add_event_form.dart

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
6767
key: _form,
6868
child: Column(
6969
mainAxisSize: MainAxisSize.min,
70+
crossAxisAlignment: CrossAxisAlignment.start,
7071
children: [
7172
TextFormField(
7273
controller: _titleController,
@@ -255,15 +256,12 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
255256
hintText: "Event Description",
256257
),
257258
),
258-
Align(
259-
alignment: Alignment.centerLeft,
260-
child: Text(
261-
'Repeat',
262-
style: TextStyle(
263-
color: AppColors.black,
264-
fontWeight: FontWeight.w500,
265-
fontSize: 17,
266-
),
259+
Text(
260+
'Repeat',
261+
style: TextStyle(
262+
color: AppColors.black,
263+
fontWeight: FontWeight.w500,
264+
fontSize: 17,
267265
),
268266
),
269267
Row(
@@ -392,7 +390,7 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
392390
crossAxisAlignment: CrossAxisAlignment.start,
393391
children: [
394392
Text(
395-
'Reoccurrence ends on: ',
393+
'${AppConstants.ltr}Reoccurrence ends on: ',
396394
style: TextStyle(
397395
color: AppColors.black,
398396
fontWeight: FontWeight.w500,
@@ -519,7 +517,7 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
519517
Row(
520518
children: [
521519
Text(
522-
"Event Color: ",
520+
"${AppConstants.ltr}Event Color: ",
523521
style: TextStyle(
524522
color: AppColors.black,
525523
fontSize: 17,
@@ -537,9 +535,11 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
537535
SizedBox(
538536
height: 15,
539537
),
540-
CustomButton(
541-
onTap: _createEvent,
542-
title: widget.event == null ? "Add Event" : "Update Event",
538+
Center(
539+
child: CustomButton(
540+
onTap: _createEvent,
541+
title: widget.event == null ? "Add Event" : "Update Event",
542+
),
543543
),
544544
],
545545
),

example/lib/widgets/calendar_configs.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:calendar_view/calendar_view.dart';
2+
import 'package:example/constants.dart';
23
import 'package:flutter/material.dart';
34

45
import '../app_colors.dart';
@@ -43,7 +44,7 @@ class CalendarConfig extends StatelessWidget {
4344
crossAxisAlignment: CrossAxisAlignment.start,
4445
children: [
4546
Text(
46-
"Active View:",
47+
"${AppConstants.ltr}Active View:",
4748
style: TextStyle(
4849
fontSize: 20.0,
4950
color: AppColors.black,
@@ -89,7 +90,7 @@ class CalendarConfig extends StatelessWidget {
8990
height: 40,
9091
),
9192
Text(
92-
"Add Event: ",
93+
"${AppConstants.ltr}Add Event: ",
9394
style: TextStyle(
9495
fontSize: 20.0,
9596
color: AppColors.black,

example/lib/widgets/day_view_widget.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:calendar_view/calendar_view.dart';
2+
import 'package:example/constants.dart';
23
import 'package:flutter/material.dart';
34

45
import '../pages/event_details_page.dart';
@@ -23,7 +24,10 @@ class DayViewWidget extends StatelessWidget {
2324
heightPerMinute: 3,
2425
timeLineBuilder: _timeLineBuilder,
2526
scrollPhysics: const BouncingScrollPhysics(),
26-
eventArranger: SideEventArranger(maxWidth: 30),
27+
eventArranger: SideEventArranger(
28+
maxWidth: 30,
29+
directionality: Directionality.of(context),
30+
),
2731
hourIndicatorSettings: HourIndicatorSettings(
2832
color: Theme.of(context).dividerColor,
2933
),
@@ -71,9 +75,10 @@ class DayViewWidget extends StatelessWidget {
7175
Positioned.fill(
7276
top: -8,
7377
right: 8,
78+
left: 8,
7479
child: Text(
7580
"${date.hour}:${date.minute}",
76-
textAlign: TextAlign.right,
81+
textAlign: TextAlign.center,
7782
style: TextStyle(
7883
color: Colors.black.withAlpha(50),
7984
fontStyle: FontStyle.italic,
@@ -92,9 +97,10 @@ class DayViewWidget extends StatelessWidget {
9297
Positioned.fill(
9398
top: -8,
9499
right: 8,
100+
left: 8,
95101
child: Text(
96-
"$hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}",
97-
textAlign: TextAlign.right,
102+
"${AppConstants.ltr} $hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}",
103+
textAlign: TextAlign.center,
98104
),
99105
),
100106
],

example/lib/widgets/week_view_widget.dart

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:calendar_view/calendar_view.dart';
2+
import 'package:example/constants.dart';
23
import 'package:flutter/material.dart';
34

45
import '../pages/event_details_page.dart';
@@ -14,14 +15,27 @@ class WeekViewWidget extends StatelessWidget {
1415
return WeekView(
1516
key: state,
1617
width: width,
18+
headerStringBuilder: (DateTime date, {DateTime? secondaryDate}) =>
19+
_weekStringBuilder(
20+
date,
21+
secondaryDate: secondaryDate,
22+
textDirection: Directionality.of(context),
23+
),
1724
showWeekends: true,
1825
showLiveTimeLineInAllDays: true,
19-
eventArranger: SideEventArranger(maxWidth: 30),
20-
timeLineWidth: 65,
26+
eventArranger: SideEventArranger(
27+
maxWidth: 30,
28+
directionality: Directionality.of(context),
29+
),
30+
timeLineWidth: 68,
2131
scrollPhysics: const BouncingScrollPhysics(),
2232
liveTimeIndicatorSettings: LiveTimeIndicatorSettings(
2333
color: Colors.redAccent,
34+
timeBackgroundViewWidth: 68,
35+
offset: 0,
2436
showTime: true,
37+
showBullet: true,
38+
showTimeBackgroundView: true,
2539
),
2640
onTimestampTap: (date) {
2741
SnackBar snackBar = SnackBar(
@@ -45,4 +59,19 @@ class WeekViewWidget extends StatelessWidget {
4559
},
4660
);
4761
}
62+
63+
// TODO(Shubham): Include in readme to guide how to support RTL for string like below
64+
String _weekStringBuilder(DateTime date,
65+
{DateTime? secondaryDate, TextDirection? textDirection}) {
66+
final dateString = "${date.day} / ${date.month} / ${date.year}";
67+
final secondaryDateString = secondaryDate != null
68+
? "${secondaryDate.day} / ${secondaryDate.month} / ${secondaryDate.year}"
69+
: "";
70+
71+
if (textDirection == TextDirection.rtl) {
72+
return "${AppConstants.ltr}${secondaryDateString} to ${dateString}";
73+
} else {
74+
return "${AppConstants.ltr}${dateString} to ${secondaryDateString}";
75+
}
76+
}
4877
}

lib/src/components/_internal_components.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ class _LiveTimeIndicatorState extends State<LiveTimeIndicator> {
9898
/// to set dy offset of live time indicator
9999
final startMinutes = widget.startHour * 60;
100100

101+
/// To support LTR & RTL we need to manage X position of point-1 to draw line
102+
/// according to position of timeline add and subtract its width
103+
final offsetX = Directionality.of(context) == TextDirection.ltr
104+
? widget.liveTimeIndicatorSettings.offset + widget.timeLineWidth
105+
: widget.liveTimeIndicatorSettings.offset - widget.timeLineWidth;
106+
101107
/// Check if live time is not between startHour and endHour if it is then
102108
/// don't show live time indicator
103109
///
@@ -111,9 +117,10 @@ class _LiveTimeIndicatorState extends State<LiveTimeIndicator> {
111117
size: Size(widget.width, widget.liveTimeIndicatorSettings.height),
112118
painter: CurrentTimeLinePainter(
113119
color: widget.liveTimeIndicatorSettings.color,
120+
textDirection: Directionality.of(context),
114121
height: widget.liveTimeIndicatorSettings.height,
115122
offset: Offset(
116-
widget.timeLineWidth + widget.liveTimeIndicatorSettings.offset,
123+
offsetX,
117124
(_currentTime.getTotalMinutes - startMinutes) *
118125
widget.heightPerMinute,
119126
),

lib/src/components/day_view_components.dart

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:flutter/material.dart';
66

77
import '../calendar_event_data.dart';
8+
import '../constants.dart';
89
import '../extensions.dart';
910
import '../typedefs.dart';
1011

@@ -63,7 +64,6 @@ class RoundedEventTile extends StatelessWidget {
6364
borderRadius: borderRadius,
6465
),
6566
child: Column(
66-
crossAxisAlignment: CrossAxisAlignment.start,
6767
mainAxisSize: MainAxisSize.min,
6868
children: [
6969
if (title.isNotEmpty)
@@ -97,6 +97,7 @@ class RoundedEventTile extends StatelessWidget {
9797
Expanded(
9898
child: Text(
9999
"+${totalEvents - 1} more",
100+
textAlign: TextAlign.center,
100101
style: (descriptionStyle ??
101102
TextStyle(
102103
color: backgroundColor.accent.withAlpha(200),
@@ -134,17 +135,19 @@ class DefaultTimeLineMark extends StatelessWidget {
134135
final timeString = (timeStringBuilder != null)
135136
? timeStringBuilder!(date)
136137
: date.minute != 0
137-
? "$hour:${date.minute}"
138-
: "$hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}";
138+
? "${Constants.ltr}$hour:${date.minute}"
139+
: "${Constants.ltr}$hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}";
139140
return Transform.translate(
140141
offset: Offset(0, -7.5),
141142
child: Padding(
142-
padding: const EdgeInsets.only(right: 7.0),
143+
padding: const EdgeInsets.only(right: 7.0, left: 7.0),
143144
child: Text(
144145
timeString,
145-
textAlign: TextAlign.right,
146+
textAlign: Directionality.of(context) == TextDirection.ltr
147+
? TextAlign.right
148+
: TextAlign.left,
146149
style: markingStyle ??
147-
TextStyle(
150+
const TextStyle(
148151
fontSize: 15.0,
149152
),
150153
),
@@ -212,14 +215,19 @@ class FullDayEventView<T> extends StatelessWidget {
212215
margin: const EdgeInsets.all(5.0),
213216
padding: const EdgeInsets.all(1.0),
214217
height: 24,
215-
child: Text(
216-
events[index].title,
217-
style: titleStyle ??
218-
TextStyle(
219-
fontSize: 16,
220-
color: events[index].color.accent,
221-
),
222-
maxLines: 1,
218+
child: Align(
219+
alignment: Directionality.of(context) == TextDirection.ltr
220+
? Alignment.centerLeft
221+
: Alignment.centerRight,
222+
child: Text(
223+
events[index].title,
224+
style: titleStyle ??
225+
TextStyle(
226+
fontSize: 16,
227+
color: events[index].color.accent,
228+
),
229+
maxLines: 1,
230+
),
223231
),
224232
decoration: BoxDecoration(
225233
borderRadius: BorderRadius.circular(5),

lib/src/constants.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Constants {
1010

1111
static final Random _random = Random();
1212
static final int _maxColor = 256;
13+
static final ltr = '\u202A'; // Use this to force text direction LTR
1314

1415
static const int hoursADay = 24;
1516
static const int minutesADay = 1440;

lib/src/day_view/_internal_day_view_page.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ class _InternalDayViewPageState<T extends Object?>
269269
widget.halfHourIndicatorSettings.dashSpaceWidth,
270270
startHour: widget.startHour,
271271
endHour: widget.endHour,
272+
textDirection: Directionality.of(context),
272273
),
273274
),
274275
if (widget.showQuarterHours)
@@ -287,6 +288,8 @@ class _InternalDayViewPageState<T extends Object?>
287288
widget.quarterHourIndicatorSettings.dashWidth,
288289
dashSpaceWidth: widget
289290
.quarterHourIndicatorSettings.dashSpaceWidth,
291+
textDirection: Directionality.of(context),
292+
timelineWidth: widget.timeLineWidth,
290293
),
291294
),
292295
widget.dayDetectorBuilder(
@@ -297,7 +300,9 @@ class _InternalDayViewPageState<T extends Object?>
297300
minuteSlotSize: widget.minuteSlotSize,
298301
),
299302
Align(
300-
alignment: Alignment.centerRight,
303+
alignment: Directionality.of(context) == TextDirection.ltr
304+
? Alignment.centerRight
305+
: Alignment.centerLeft,
301306
child: EventGenerator<T>(
302307
height: widget.height,
303308
date: widget.date,

lib/src/day_view/day_view.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
566566
_halfHourIndicatorSettings = widget.halfHourIndicatorSettings ??
567567
HourIndicatorSettings(
568568
height: widget.heightPerMinute,
569+
lineStyle: LineStyle.dashed,
569570
color: Constants.defaultBorderColor,
570571
offset: 5,
571572
);
@@ -731,10 +732,13 @@ class DayViewState<T extends Object?> extends State<DayView<T>> {
731732
int startHour,
732733
int endHour,
733734
) {
735+
final directionality = Directionality.of(context);
734736
return HourLinePainter(
735737
lineColor: lineColor,
736738
lineHeight: lineHeight,
737-
offset: offset,
739+
timelineWidth: widget.timeLineWidth,
740+
textDirection: directionality,
741+
offset: directionality == TextDirection.ltr ? offset : 0,
738742
minuteHeight: minuteHeight,
739743
verticalLineOffset: verticalLineOffset,
740744
showVerticalLine: showVerticalLine,

0 commit comments

Comments
 (0)