Skip to content

Commit

Permalink
Implement timeline calendar view. (#195)
Browse files Browse the repository at this point in the history
* Implement timeline calender view

* Fix typo

* Fix lint and build

* Fix lint

* Mr changes

* Mr changes

---------

Co-authored-by: kaushik <[email protected]>
  • Loading branch information
kaushiksaliya and kaushik authored Jan 6, 2025
1 parent 5ec663c commit 7ee7493
Show file tree
Hide file tree
Showing 9 changed files with 711 additions and 146 deletions.
3 changes: 3 additions & 0 deletions app/assets/images/ic_calendar_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions app/lib/gen/assets.gen.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

133 changes: 133 additions & 0 deletions app/lib/ui/flow/journey/calender/horizontal_calendar_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:style/extenstions/context_extenstions.dart';
import 'package:style/extenstions/date_extenstions.dart';
import 'package:style/text/app_text_dart.dart';
import 'package:yourspace_flutter/domain/extenstions/date_formatter.dart';
import 'package:yourspace_flutter/ui/flow/journey/calender/three_page_scroller.dart';

class HorizontalCalendarView extends StatefulWidget {
final DateTime weekStartDate;
final DateTime selectedDate;
final ValueChanged<int> onSwipeWeek;
final void Function(DateTime date) onTap;

const HorizontalCalendarView({
super.key,
required this.weekStartDate,
required this.selectedDate,
required this.onSwipeWeek,
required this.onTap,
});

@override
State<HorizontalCalendarView> createState() => _HorizontalCalendarViewState();
}

class _HorizontalCalendarViewState extends State<HorizontalCalendarView> {
@override
Widget build(BuildContext context) {
final previousWeekDate =
widget.weekStartDate.subtract(const Duration(days: 7));
final nextWeekDate = widget.weekStartDate.add(const Duration(days: 7));

return Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 12, bottom: 16),
child: ThreePageScroller(
current: _weekDays(startDate: widget.weekStartDate),
previous: _weekDays(startDate: previousWeekDate),
next: _weekDays(startDate: nextWeekDate),
onPageChanged: (direction) => widget.onSwipeWeek(direction),
),
),
Divider(thickness: 1, height: 1, color: context.colorScheme.outline)
],
);
}

Widget _weekDays({
required DateTime startDate,
}) {
final weekDays = <DateTime>[];
for (var i = 0; i < 7; i++) {
weekDays.add(startDate.add(Duration(days: i)));
}
final isWeekAfter = weekDays.first.isAfter(DateTime.now());

return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Row(
children: weekDays.map(
(date) {
return isWeekAfter
? const SizedBox()
: _weekDayItem(date: date, context: context);
},
).toList(),
),
);
}

Widget _weekDayItem({required BuildContext context, required DateTime date}) {
final bool isSelected = date.isSameDay(widget.selectedDate);
final isDayAfter = date.isAfter(DateTime.now());
final textColor = isDayAfter
? context.colorScheme.textDisabled
: context.colorScheme.textPrimary;

return Expanded(
child: GestureDetector(
onTap: () => widget.onTap(date),
child: LayoutBuilder(
builder: (context, constraints) {
return Center(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 6),
width: [48, constraints.maxWidth].reduce(min).toDouble(),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: isSelected
? context.colorScheme.primary
: Colors.transparent,
border: Border.all(
color: (isSelected || !date.isToday)
? Colors.transparent
: context.colorScheme.containerLow,
width: 1,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
date.format(context, DateFormatType.weekShort),
style: AppTextStyle.caption.copyWith(
color: isSelected
? context.colorScheme.textInversePrimary
: context.colorScheme.textDisabled,
),
textScaler: TextScaler.noScaling,
),
const SizedBox(height: 2),
Text(
date.day.toString(),
style: AppTextStyle.bodyBold.copyWith(
color: isSelected
? context.colorScheme.textInversePrimary
: textColor,
),
textScaler: TextScaler.noScaling,
),
],
),
),
);
},
),
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:style/extenstions/date_extenstions.dart';

part 'horizontal_calendar_view_model.freezed.dart';

final horizontalCalendarViewStateProvider = StateNotifierProvider.autoDispose<
HorizontalCalendarViewModel, CalendarViewState>((ref) {
return HorizontalCalendarViewModel();
});

class HorizontalCalendarViewModel extends StateNotifier<CalendarViewState> {
HorizontalCalendarViewModel()
: super(
CalendarViewState(
weekStartDate: DateTime.now().startOfDay,
selectedDate: DateTime.now().startOfDay,
),
) {
setCurrentWeekStartDate();
}

void setCurrentWeekStartDate() {
final currentDate = state.selectedDate;
// Monday
final dayOfWeek = currentDate.weekday;
final daysToSubtract = dayOfWeek == 1 ? 0 : dayOfWeek - 1;
final currentWeekStartDate =
currentDate.subtract(Duration(days: daysToSubtract));
state = state.copyWith(weekStartDate: currentWeekStartDate);
}

void onSwipeWeek(int direction) {
final currentWeekStartDate = state.weekStartDate;
final newWeekStartDate =
currentWeekStartDate.add(Duration(days: direction * 7));
if (newWeekStartDate.isAfter(DateTime.now().startOfDay)) {
return;
}
state = state.copyWith(weekStartDate: newWeekStartDate);
setContainsToday();
}

void setSelectedDate(DateTime? date, {bool isPickerDate = false}) {
state = state.copyWith(
selectedDate: date?.startOfDay ?? DateTime.now().startOfDay);
if (isPickerDate && date != null) {
onSelectDateFromDatePicker(date);
}
}

void onSelectDateFromDatePicker(DateTime date) {
state = state.copyWith(weekStartDate: date.startOfWeek);
setContainsToday();
}

void setContainsToday() {
final DateTime today = DateTime.now();
final DateTime endOfWeek = state.weekStartDate.add(const Duration(days: 6));

final containsToday =
today.isAfter(state.weekStartDate) && today.isBefore(endOfWeek);
state = state.copyWith(containsToday: containsToday);
}

void goToToday() {
setSelectedDate(DateTime.now());
setCurrentWeekStartDate();
setContainsToday();
}
}

@freezed
class CalendarViewState with _$CalendarViewState {
const factory CalendarViewState({
required DateTime selectedDate,
required DateTime weekStartDate,
@Default(true) bool containsToday,
}) = _CalendarViewState;
}
Loading

0 comments on commit 7ee7493

Please sign in to comment.