dart format
This commit is contained in:
@@ -11,7 +11,11 @@ class AppointmentTile extends StatelessWidget {
|
||||
final Appointment appointment;
|
||||
final bool crossedOut;
|
||||
|
||||
const AppointmentTile({super.key, required this.appointment, this.crossedOut = false});
|
||||
const AppointmentTile({
|
||||
super.key,
|
||||
required this.appointment,
|
||||
this.crossedOut = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -56,11 +60,15 @@ class AppointmentTile extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
] else ...[
|
||||
for (final line in description
|
||||
.split('\n')
|
||||
.where((p) => p.isNotEmpty)
|
||||
.take(2))
|
||||
_ScaledLine(text: line, fontSize: kAppointmentBodyFontSize),
|
||||
for (final line
|
||||
in description
|
||||
.split('\n')
|
||||
.where((p) => p.isNotEmpty)
|
||||
.take(2))
|
||||
_ScaledLine(
|
||||
text: line,
|
||||
fontSize: kAppointmentBodyFontSize,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
@@ -72,7 +80,10 @@ class AppointmentTile extends StatelessWidget {
|
||||
borderRadius: _radius,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(width: 2, color: Colors.red.withAlpha(200)),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: Colors.red.withAlpha(200),
|
||||
),
|
||||
borderRadius: _radius,
|
||||
),
|
||||
child: CustomPaint(painter: CrossPainter()),
|
||||
@@ -114,7 +125,10 @@ class _AdaptiveTitle extends StatelessWidget {
|
||||
builder: (context, constraints) {
|
||||
// Probe at the minimum size: if even that overflows, we have to ellipsize.
|
||||
final probe = TextPainter(
|
||||
text: TextSpan(text: text, style: baseStyle.copyWith(fontSize: minFontSize)),
|
||||
text: TextSpan(
|
||||
text: text,
|
||||
style: baseStyle.copyWith(fontSize: minFontSize),
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
maxLines: 1,
|
||||
textScaler: textScaler,
|
||||
@@ -131,12 +145,7 @@ class _AdaptiveTitle extends StatelessWidget {
|
||||
return FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
text,
|
||||
style: baseStyle,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
),
|
||||
child: Text(text, style: baseStyle, maxLines: 1, softWrap: false),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -187,24 +196,17 @@ class _ScaledLine extends StatelessWidget {
|
||||
final String text;
|
||||
final double fontSize;
|
||||
|
||||
const _ScaledLine({
|
||||
required this.text,
|
||||
required this.fontSize,
|
||||
});
|
||||
const _ScaledLine({required this.text, required this.fontSize});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: fontSize,
|
||||
height: 1.1,
|
||||
),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
),
|
||||
);
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(color: Colors.white, fontSize: fontSize, height: 1.1),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@ class _DayHeaderStrip extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Row(
|
||||
children: [
|
||||
SizedBox(width: rulerWidth),
|
||||
for (var d = 0; d < 5; d++)
|
||||
Expanded(
|
||||
child: _DayHeaderCell(
|
||||
date: weekStart.add(Duration(days: d)),
|
||||
today: today,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
children: [
|
||||
SizedBox(width: rulerWidth),
|
||||
for (var d = 0; d < 5; d++)
|
||||
Expanded(
|
||||
child: _DayHeaderCell(
|
||||
date: weekStart.add(Duration(days: d)),
|
||||
today: today,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class _DayHeaderCell extends StatelessWidget {
|
||||
@@ -37,7 +37,10 @@ class _DayHeaderCell extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final isToday = date.isSameDay(today);
|
||||
final dayName = DateFormat('EE', Localizations.localeOf(context).toString()).format(date).toUpperCase();
|
||||
final dayName = DateFormat(
|
||||
'EE',
|
||||
Localizations.localeOf(context).toString(),
|
||||
).format(date).toUpperCase();
|
||||
|
||||
final accent = theme.colorScheme.primary;
|
||||
final onAccent = theme.colorScheme.onPrimary;
|
||||
|
||||
@@ -18,20 +18,30 @@ class _OutsideHoursStrip extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final outside = partitionAppointmentsForWeek(appointments, weekStart).outside;
|
||||
final outside = partitionAppointmentsForWeek(
|
||||
appointments,
|
||||
weekStart,
|
||||
).outside;
|
||||
if (outside.every((day) => day.isEmpty)) return const SizedBox.shrink();
|
||||
|
||||
final theme = Theme.of(context);
|
||||
final maxChipsPerDay = outside
|
||||
.map((day) => day.length > kOutsideChipsMaxVisible ? kOutsideChipsMaxVisible : day.length)
|
||||
.map(
|
||||
(day) => day.length > kOutsideChipsMaxVisible
|
||||
? kOutsideChipsMaxVisible
|
||||
: day.length,
|
||||
)
|
||||
.fold<int>(0, (m, c) => c > m ? c : m);
|
||||
final stripHeight = kOutsideStripVerticalPadding * 2 +
|
||||
final stripHeight =
|
||||
kOutsideStripVerticalPadding * 2 +
|
||||
maxChipsPerDay * kOutsideChipHeight +
|
||||
(maxChipsPerDay > 1 ? (maxChipsPerDay - 1) * kOutsideChipSpacing : 0);
|
||||
|
||||
return Container(
|
||||
color: theme.colorScheme.surfaceContainerLowest,
|
||||
padding: const EdgeInsets.symmetric(vertical: kOutsideStripVerticalPadding),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: kOutsideStripVerticalPadding,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: stripHeight - kOutsideStripVerticalPadding * 2,
|
||||
child: Row(
|
||||
@@ -72,27 +82,29 @@ class _OutsideDayColumn extends StatelessWidget {
|
||||
for (var i = 0; i < hidden.length; i++) {
|
||||
if (i > 0) tiles.add(const Divider(height: 1));
|
||||
final apt = hidden[i];
|
||||
tiles.add(ListTile(
|
||||
leading: Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
color: apt.color,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
tiles.add(
|
||||
ListTile(
|
||||
leading: Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
color: apt.color,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
apt.subject,
|
||||
style: isCrossedOut(apt)
|
||||
? const TextStyle(decoration: TextDecoration.lineThrough)
|
||||
: null,
|
||||
),
|
||||
subtitle: Text(_subtitleFor(apt)),
|
||||
onTap: () {
|
||||
Navigator.of(sheetCtx).pop();
|
||||
onAppointmentTap(apt);
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
apt.subject,
|
||||
style: isCrossedOut(apt)
|
||||
? const TextStyle(decoration: TextDecoration.lineThrough)
|
||||
: null,
|
||||
),
|
||||
subtitle: Text(_subtitleFor(apt)),
|
||||
onTap: () {
|
||||
Navigator.of(sheetCtx).pop();
|
||||
onAppointmentTap(apt);
|
||||
},
|
||||
));
|
||||
);
|
||||
}
|
||||
return tiles;
|
||||
},
|
||||
|
||||
@@ -34,11 +34,7 @@ class _WeekGrid extends StatelessWidget {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_PeriodRuler(
|
||||
schedule: schedule,
|
||||
layout: layout,
|
||||
width: rulerWidth,
|
||||
),
|
||||
_PeriodRuler(schedule: schedule, layout: layout, width: rulerWidth),
|
||||
for (var d = 0; d < 5; d++)
|
||||
Expanded(
|
||||
child: _DayColumn(
|
||||
@@ -112,7 +108,11 @@ class _PeriodLabel extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Icon(Icons.coffee_outlined, size: 12, color: secondaryTextColor.withAlpha(180)),
|
||||
child: Icon(
|
||||
Icons.coffee_outlined,
|
||||
size: 12,
|
||||
color: secondaryTextColor.withAlpha(180),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -207,27 +207,49 @@ class _DayColumn extends StatelessWidget {
|
||||
required this.onCreateEvent,
|
||||
});
|
||||
|
||||
bool _overlapsExistingAppointment(DateTime start, DateTime end, List<Appointment> dayAppts) {
|
||||
bool _overlapsExistingAppointment(
|
||||
DateTime start,
|
||||
DateTime end,
|
||||
List<Appointment> dayAppts,
|
||||
) {
|
||||
for (final a in dayAppts) {
|
||||
if (a.endTime.isAfter(start) && a.startTime.isBefore(end)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _handleLongPress(LongPressStartDetails details, List<Appointment> dayAppts) {
|
||||
void _handleLongPress(
|
||||
LongPressStartDetails details,
|
||||
List<Appointment> dayAppts,
|
||||
) {
|
||||
if (onCreateEvent == null) return;
|
||||
final period = layout.periodAtY(details.localPosition.dy);
|
||||
if (period == null) return;
|
||||
|
||||
final start = DateTime(date.year, date.month, date.day, period.start.hour, period.start.minute);
|
||||
final end = DateTime(date.year, date.month, date.day, period.end.hour, period.end.minute);
|
||||
final start = DateTime(
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
period.start.hour,
|
||||
period.start.minute,
|
||||
);
|
||||
final end = DateTime(
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
period.end.hour,
|
||||
period.end.minute,
|
||||
);
|
||||
if (_overlapsExistingAppointment(start, end, dayAppts)) return;
|
||||
|
||||
HapticFeedback.mediumImpact();
|
||||
onCreateEvent!(start, end);
|
||||
}
|
||||
|
||||
void _showOverflowSheet(BuildContext context, List<Appointment> appointments) {
|
||||
void _showOverflowSheet(
|
||||
BuildContext context,
|
||||
List<Appointment> appointments,
|
||||
) {
|
||||
final sorted = [...appointments]
|
||||
..sort((a, b) => a.startTime.compareTo(b.startTime));
|
||||
showDetailsBottomSheet(
|
||||
@@ -237,27 +259,29 @@ class _DayColumn extends StatelessWidget {
|
||||
for (var i = 0; i < sorted.length; i++) {
|
||||
if (i > 0) tiles.add(const Divider(height: 1));
|
||||
final apt = sorted[i];
|
||||
tiles.add(ListTile(
|
||||
leading: Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
color: apt.color,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
tiles.add(
|
||||
ListTile(
|
||||
leading: Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
color: apt.color,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
apt.subject,
|
||||
style: isCrossedOut(apt)
|
||||
? const TextStyle(decoration: TextDecoration.lineThrough)
|
||||
: null,
|
||||
),
|
||||
subtitle: Text(_overflowSubtitle(apt)),
|
||||
onTap: () {
|
||||
Navigator.of(sheetContext).pop();
|
||||
onAppointmentTap(apt);
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
apt.subject,
|
||||
style: isCrossedOut(apt)
|
||||
? const TextStyle(decoration: TextDecoration.lineThrough)
|
||||
: null,
|
||||
),
|
||||
subtitle: Text(_overflowSubtitle(apt)),
|
||||
onTap: () {
|
||||
Navigator.of(sheetContext).pop();
|
||||
onAppointmentTap(apt);
|
||||
},
|
||||
));
|
||||
);
|
||||
}
|
||||
return tiles;
|
||||
},
|
||||
@@ -288,46 +312,53 @@ class _DayColumn extends StatelessWidget {
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onLongPressStart: (details) => _handleLongPress(details, dayAppointments),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: isToday ? theme.colorScheme.primary.withAlpha(14) : null,
|
||||
border: Border(left: BorderSide(color: theme.dividerColor.withAlpha(90), width: 0.5)),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final width = constraints.maxWidth;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
for (final period in schedule.periods)
|
||||
Positioned(
|
||||
top: layout.topOf(period),
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
height: 0.5,
|
||||
color: theme.dividerColor.withAlpha(60),
|
||||
decoration: BoxDecoration(
|
||||
color: isToday ? theme.colorScheme.primary.withAlpha(14) : null,
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: theme.dividerColor.withAlpha(90),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final width = constraints.maxWidth;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
for (final period in schedule.periods)
|
||||
Positioned(
|
||||
top: layout.topOf(period),
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
height: 0.5,
|
||||
color: theme.dividerColor.withAlpha(60),
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final region in dayRegions)
|
||||
Positioned(
|
||||
top: layout.yOfDateTime(region.start),
|
||||
height: (layout.yOfDateTime(region.end) -
|
||||
layout.yOfDateTime(region.start))
|
||||
.clamp(0, double.infinity),
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: TimeRegionTile(region: region.region),
|
||||
),
|
||||
for (final cell in laidOut)
|
||||
Positioned(
|
||||
top: layout.yOfDateTime(cell.startTime),
|
||||
height: (layout.yOfDateTime(cell.endTime) -
|
||||
layout.yOfDateTime(cell.startTime))
|
||||
.clamp(0, double.infinity),
|
||||
left: cell.lane * width / cell.laneCount,
|
||||
width: width / cell.laneCount,
|
||||
child: switch (cell) {
|
||||
LaidOutAppointment(:final appointment) => GestureDetector(
|
||||
for (final region in dayRegions)
|
||||
Positioned(
|
||||
top: layout.yOfDateTime(region.start),
|
||||
height:
|
||||
(layout.yOfDateTime(region.end) -
|
||||
layout.yOfDateTime(region.start))
|
||||
.clamp(0, double.infinity),
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: TimeRegionTile(region: region.region),
|
||||
),
|
||||
for (final cell in laidOut)
|
||||
Positioned(
|
||||
top: layout.yOfDateTime(cell.startTime),
|
||||
height:
|
||||
(layout.yOfDateTime(cell.endTime) -
|
||||
layout.yOfDateTime(cell.startTime))
|
||||
.clamp(0, double.infinity),
|
||||
left: cell.lane * width / cell.laneCount,
|
||||
width: width / cell.laneCount,
|
||||
child: switch (cell) {
|
||||
LaidOutAppointment(:final appointment) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => onAppointmentTap(appointment),
|
||||
child: AppointmentTile(
|
||||
@@ -335,25 +366,27 @@ class _DayColumn extends StatelessWidget {
|
||||
crossedOut: isCrossedOut(appointment),
|
||||
),
|
||||
),
|
||||
LaidOutOverflow(:final appointments) => GestureDetector(
|
||||
LaidOutOverflow(:final appointments) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () =>
|
||||
_showOverflowSheet(context, appointments),
|
||||
onTap: () => _showOverflowSheet(context, appointments),
|
||||
child: _OverflowTile(count: appointments.length),
|
||||
),
|
||||
},
|
||||
),
|
||||
if (isToday)
|
||||
ValueListenableBuilder<DateTime>(
|
||||
valueListenable: nowNotifier,
|
||||
builder: (_, now, child) =>
|
||||
_CurrentTimeMarker(now: now, layout: layout, theme: theme),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
if (isToday)
|
||||
ValueListenableBuilder<DateTime>(
|
||||
valueListenable: nowNotifier,
|
||||
builder: (_, now, child) => _CurrentTimeMarker(
|
||||
now: now,
|
||||
layout: layout,
|
||||
theme: theme,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -376,8 +409,7 @@ class _CurrentTimeMarker extends StatelessWidget {
|
||||
final tMin = now.hour * 60 + now.minute;
|
||||
final firstStart =
|
||||
periods.first.start.hour * 60 + periods.first.start.minute;
|
||||
final lastEnd =
|
||||
periods.last.end.hour * 60 + periods.last.end.minute;
|
||||
final lastEnd = periods.last.end.hour * 60 + periods.last.end.minute;
|
||||
if (tMin < firstStart || tMin > lastEnd) return const SizedBox.shrink();
|
||||
|
||||
final y = layout.yOfDateTime(now);
|
||||
@@ -392,10 +424,7 @@ class _CurrentTimeMarker extends StatelessWidget {
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Container(
|
||||
height: 2,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
Container(height: 2, color: theme.colorScheme.primary),
|
||||
Positioned(
|
||||
top: -3,
|
||||
left: -4,
|
||||
@@ -456,7 +485,10 @@ class _OverflowTile extends StatelessWidget {
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
@@ -72,7 +72,8 @@ class CustomWorkWeekCalendarState extends State<CustomWorkWeekCalendar> {
|
||||
_firstMonday = _mondayOf(widget.minDate);
|
||||
final lastMonday = _mondayOf(widget.maxDate);
|
||||
_totalWeeks = lastMonday.difference(_firstMonday).inDays ~/ 7 + 1;
|
||||
_currentWeekIndex = _mondayOf(widget.initialDate).difference(_firstMonday).inDays ~/ 7;
|
||||
_currentWeekIndex =
|
||||
_mondayOf(widget.initialDate).difference(_firstMonday).inDays ~/ 7;
|
||||
_pageController = PageController(initialPage: _currentWeekIndex);
|
||||
_nowNotifier = ValueNotifier<DateTime>(DateTime.now());
|
||||
|
||||
@@ -113,7 +114,9 @@ class CustomWorkWeekCalendarState extends State<CustomWorkWeekCalendar> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final visibleWeekStart = _firstMonday.add(Duration(days: _currentWeekIndex * 7));
|
||||
final visibleWeekStart = _firstMonday.add(
|
||||
Duration(days: _currentWeekIndex * 7),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
@@ -168,13 +171,13 @@ class CustomWorkWeekCalendarState extends State<CustomWorkWeekCalendar> {
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final periods = widget.schedule.periods;
|
||||
final lessonCount =
|
||||
periods.where((p) => !p.isBreak).length;
|
||||
final lessonCount = periods.where((p) => !p.isBreak).length;
|
||||
final breakCount = periods.length - lessonCount;
|
||||
final available =
|
||||
constraints.maxHeight - breakCount * kBreakBlockHeight;
|
||||
final fitLessonH =
|
||||
lessonCount > 0 ? available / lessonCount : kLessonBlockMinHeight;
|
||||
final fitLessonH = lessonCount > 0
|
||||
? available / lessonCount
|
||||
: kLessonBlockMinHeight;
|
||||
final lessonH = fitLessonH < kLessonBlockMinHeight
|
||||
? kLessonBlockMinHeight
|
||||
: fitLessonH;
|
||||
@@ -194,11 +197,18 @@ class CustomWorkWeekCalendarState extends State<CustomWorkWeekCalendar> {
|
||||
itemCount: _totalWeeks,
|
||||
onPageChanged: (index) {
|
||||
setState(() => _currentWeekIndex = index);
|
||||
final weekStart = _firstMonday.add(Duration(days: index * 7));
|
||||
widget.onWeekChanged(weekStart, weekStart.add(const Duration(days: 4)));
|
||||
final weekStart = _firstMonday.add(
|
||||
Duration(days: index * 7),
|
||||
);
|
||||
widget.onWeekChanged(
|
||||
weekStart,
|
||||
weekStart.add(const Duration(days: 4)),
|
||||
);
|
||||
},
|
||||
itemBuilder: (_, weekIndex) {
|
||||
final weekStart = _firstMonday.add(Duration(days: weekIndex * 7));
|
||||
final weekStart = _firstMonday.add(
|
||||
Duration(days: weekIndex * 7),
|
||||
);
|
||||
return _WeekGrid(
|
||||
weekStart: weekStart,
|
||||
schedule: widget.schedule,
|
||||
|
||||
@@ -22,45 +22,61 @@ class SpecialRegionsBuilder {
|
||||
});
|
||||
|
||||
List<TimeRegion> build() {
|
||||
final lastMonday = DateTime.now().subtract(const Duration(days: 14)).nextWeekday(DateTime.monday);
|
||||
final lastMonday = DateTime.now()
|
||||
.subtract(const Duration(days: 14))
|
||||
.nextWeekday(DateTime.monday);
|
||||
|
||||
final holidayRegions = _buildHolidayRegions().toList();
|
||||
bool isInHoliday(DateTime time) => holidayRegions.any((region) => region.startTime.isSameDay(time));
|
||||
bool isInHoliday(DateTime time) =>
|
||||
holidayRegions.any((region) => region.startTime.isSameDay(time));
|
||||
|
||||
final breakRegions = schedule.periods.where((p) => p.isBreak).map((p) {
|
||||
final start = lastMonday.copyWith(hour: p.start.hour, minute: p.start.minute);
|
||||
return _breakRegion(start, p.duration);
|
||||
}).where((region) => !isInHoliday(region.startTime));
|
||||
final breakRegions = schedule.periods
|
||||
.where((p) => p.isBreak)
|
||||
.map((p) {
|
||||
final start = lastMonday.copyWith(
|
||||
hour: p.start.hour,
|
||||
minute: p.start.minute,
|
||||
);
|
||||
return _breakRegion(start, p.duration);
|
||||
})
|
||||
.where((region) => !isInHoliday(region.startTime));
|
||||
|
||||
return [
|
||||
...holidayRegions,
|
||||
...breakRegions,
|
||||
];
|
||||
return [...holidayRegions, ...breakRegions];
|
||||
}
|
||||
|
||||
Iterable<TimeRegion> _buildHolidayRegions() => holidays.result.expand((holiday) {
|
||||
final startDay = WebuntisTime.parse(holiday.startDate, 0);
|
||||
final dayCount = WebuntisTime.parse(holiday.endDate, 0).difference(startDay).inDays;
|
||||
final days = List<DateTime>.generate(dayCount, (i) => startDay.add(Duration(days: i)));
|
||||
final gridStartHour = kCalendarStartHour.floor();
|
||||
final gridStartMinute = ((kCalendarStartHour - gridStartHour) * 60).round();
|
||||
final gridEndHour = kCalendarEndHour.floor();
|
||||
final gridEndMinute = ((kCalendarEndHour - gridEndHour) * 60).round();
|
||||
return days.map((day) => TimeRegion(
|
||||
startTime: day.copyWith(hour: gridStartHour, minute: gridStartMinute),
|
||||
endTime: day.copyWith(hour: gridEndHour, minute: gridEndMinute),
|
||||
text: '$kTimeRegionHolidayPrefix${holiday.name}',
|
||||
color: disabledColor.withAlpha(50),
|
||||
iconData: Icons.holiday_village_outlined,
|
||||
));
|
||||
});
|
||||
Iterable<TimeRegion> _buildHolidayRegions() => holidays.result.expand((
|
||||
holiday,
|
||||
) {
|
||||
final startDay = WebuntisTime.parse(holiday.startDate, 0);
|
||||
final dayCount = WebuntisTime.parse(
|
||||
holiday.endDate,
|
||||
0,
|
||||
).difference(startDay).inDays;
|
||||
final days = List<DateTime>.generate(
|
||||
dayCount,
|
||||
(i) => startDay.add(Duration(days: i)),
|
||||
);
|
||||
final gridStartHour = kCalendarStartHour.floor();
|
||||
final gridStartMinute = ((kCalendarStartHour - gridStartHour) * 60).round();
|
||||
final gridEndHour = kCalendarEndHour.floor();
|
||||
final gridEndMinute = ((kCalendarEndHour - gridEndHour) * 60).round();
|
||||
return days.map(
|
||||
(day) => TimeRegion(
|
||||
startTime: day.copyWith(hour: gridStartHour, minute: gridStartMinute),
|
||||
endTime: day.copyWith(hour: gridEndHour, minute: gridEndMinute),
|
||||
text: '$kTimeRegionHolidayPrefix${holiday.name}',
|
||||
color: disabledColor.withAlpha(50),
|
||||
iconData: Icons.holiday_village_outlined,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
TimeRegion _breakRegion(DateTime start, Duration duration) => TimeRegion(
|
||||
startTime: start,
|
||||
endTime: start.add(duration),
|
||||
recurrenceRule: 'FREQ=DAILY;INTERVAL=1',
|
||||
text: kTimeRegionCenterIcon,
|
||||
color: colorScheme.primary.withAlpha(50),
|
||||
iconData: Icons.restaurant,
|
||||
);
|
||||
startTime: start,
|
||||
endTime: start.add(duration),
|
||||
recurrenceRule: 'FREQ=DAILY;INTERVAL=1',
|
||||
text: kTimeRegionCenterIcon,
|
||||
color: colorScheme.primary.withAlpha(50),
|
||||
iconData: Icons.restaurant,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ class TimeRegionTile extends StatelessWidget {
|
||||
return Container(
|
||||
color: color,
|
||||
alignment: Alignment.center,
|
||||
child: Icon(region.iconData, size: 17, color: Theme.of(context).colorScheme.primary),
|
||||
child: Icon(
|
||||
region.iconData,
|
||||
size: 17,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user