feat: add week template day selector

This commit is contained in:
Freek van de Ven 2024-07-15 16:19:16 +02:00 committed by FlutterJoey
parent 92703d536d
commit 5b54111850
3 changed files with 197 additions and 5 deletions

View file

@ -38,6 +38,8 @@ class AvailabilityTranslations {
required this.createWeekTemplate, required this.createWeekTemplate,
required this.deleteTemplateButton, required this.deleteTemplateButton,
required this.dayTemplateTitle, required this.dayTemplateTitle,
required this.weekTemplateTitle,
required this.weekTemplateDayTitle,
required this.templateTitleHintText, required this.templateTitleHintText,
required this.templateTitleLabel, required this.templateTitleLabel,
required this.templateColorLabel, required this.templateColorLabel,
@ -61,6 +63,7 @@ class AvailabilityTranslations {
required this.periodFormatter, required this.periodFormatter,
required this.monthYearFormatter, required this.monthYearFormatter,
required this.weekDayAbbreviatedFormatter, required this.weekDayAbbreviatedFormatter,
required this.weekDayFormatter,
}); });
/// AvailabilityTranslations constructor where everything is optional. /// AvailabilityTranslations constructor where everything is optional.
@ -97,6 +100,8 @@ class AvailabilityTranslations {
this.createWeekTemplate = "Create week template", this.createWeekTemplate = "Create week template",
this.deleteTemplateButton = "Delete template", this.deleteTemplateButton = "Delete template",
this.dayTemplateTitle = "Day template", this.dayTemplateTitle = "Day template",
this.weekTemplateTitle = "Week template",
this.weekTemplateDayTitle = "When",
this.templateTitleHintText = "What do you want to call this template?", this.templateTitleHintText = "What do you want to call this template?",
this.templateTitleLabel = "Template Title", this.templateTitleLabel = "Template Title",
this.templateColorLabel = "Colorlabel", this.templateColorLabel = "Colorlabel",
@ -123,6 +128,7 @@ class AvailabilityTranslations {
this.periodFormatter = _defaultPeriodFormatter, this.periodFormatter = _defaultPeriodFormatter,
this.monthYearFormatter = _defaultMonthYearFormatter, this.monthYearFormatter = _defaultMonthYearFormatter,
this.weekDayAbbreviatedFormatter = _defaultWeekDayAbbreviatedFormatter, this.weekDayAbbreviatedFormatter = _defaultWeekDayAbbreviatedFormatter,
this.weekDayFormatter = _defaultWeekDayFormatter,
this.timeFormatter = _defaultTimeFormatter, this.timeFormatter = _defaultTimeFormatter,
}); });
@ -208,6 +214,12 @@ class AvailabilityTranslations {
/// The title for the day template edit screen /// The title for the day template edit screen
final String dayTemplateTitle; final String dayTemplateTitle;
/// The title for the week template edit screen
final String weekTemplateTitle;
/// The title above the section with the week template days
final String weekTemplateDayTitle;
/// The hint text for the template title input field /// The hint text for the template title input field
final String templateTitleHintText; final String templateTitleHintText;
@ -286,12 +298,20 @@ class AvailabilityTranslations {
/// the weekday in english /// the weekday in english
final String Function(BuildContext, DateTime) weekDayAbbreviatedFormatter; final String Function(BuildContext, DateTime) weekDayAbbreviatedFormatter;
/// Gets the weekday formatted as a string
///
/// The default implementation is the full name of the weekday in english
final String Function(BuildContext, DateTime) weekDayFormatter;
/// Get the time formatted as a string /// Get the time formatted as a string
/// ///
/// The default implementation is `HH:mm` /// The default implementation is `HH:mm`
final String Function(BuildContext, TimeOfDay) timeFormatter; final String Function(BuildContext, TimeOfDay) timeFormatter;
} }
String _defaultWeekDayFormatter(BuildContext context, DateTime date) =>
_getDayName(date.weekday);
String _defaultTimeFormatter(BuildContext context, TimeOfDay time) => String _defaultTimeFormatter(BuildContext context, TimeOfDay time) =>
"${time.hour.toString().padLeft(2, '0')}:" "${time.hour.toString().padLeft(2, '0')}:"
"${time.minute.toString().padLeft(2, '0')}"; "${time.minute.toString().padLeft(2, '0')}";

View file

@ -37,11 +37,8 @@ class CalendarGrid extends StatelessWidget {
var calendarDays = var calendarDays =
_generateCalendarDays(month, days, selectedRange, colors, colorScheme); _generateCalendarDays(month, days, selectedRange, colors, colorScheme);
// get the names of the days of the week var dayNames =
var dayNames = List.generate(7, (index) { getDaysOfTheWeekAsAbbreviatedStrings(translations, context);
var day = DateTime(2024, 7, 8 + index); // this is a monday
return translations.weekDayAbbreviatedFormatter(context, day);
});
var calendarDaysRow = Row( var calendarDaysRow = Row(
children: List.generate(13, (index) { children: List.generate(13, (index) {
@ -122,6 +119,31 @@ class CalendarGrid extends StatelessWidget {
} }
} }
/// Returns the days of the week as abbreviated strings
/// The first day of the week is monday
List<String> getDaysOfTheWeekAsAbbreviatedStrings(
AvailabilityTranslations translations,
BuildContext context,
) {
var dayNames = List.generate(7, (index) {
var day = DateTime(2024, 7, 8 + index); // this is a monday
return translations.weekDayAbbreviatedFormatter(context, day);
});
return dayNames;
}
/// Returns the days of the week as strings
List<String> getDaysOfTheWeekAsStrings(
AvailabilityTranslations translations,
BuildContext context,
) {
var dayNames = List.generate(7, (index) {
var day = DateTime(2024, 7, 8 + index); // this is a monday
return translations.weekDayFormatter(context, day);
});
return dayNames;
}
/// A Special day in the calendar that needs to be displayed differently /// A Special day in the calendar that needs to be displayed differently
class CalendarDay { class CalendarDay {
/// ///

View file

@ -0,0 +1,150 @@
// ignore_for_file: avoid_positional_boolean_parameters
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/widgets/calendar_grid.dart";
import "package:flutter_availability/src/util/scope.dart";
/// A widget for selecting a day of the week
class TemplateWeekDaySelection extends StatefulWidget {
/// Creates a [TemplateWeekDaySelection]
const TemplateWeekDaySelection({
required this.onDaySelected,
super.key,
});
/// Callback for when a day is selected
final void Function(int) onDaySelected;
@override
State<TemplateWeekDaySelection> createState() =>
_TemplateWeekDaySelectionState();
}
class _TemplateWeekDaySelectionState extends State<TemplateWeekDaySelection> {
int _selectedDayIndex = 0;
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var textTheme = theme.textTheme;
var availabilityScope = AvailabilityScope.of(context);
var options = availabilityScope.options;
var translations = options.translations;
var days = getDaysOfTheWeekAsAbbreviatedStrings(translations, context);
void onDaySelected(bool selected, int index) {
if (!selected) return;
widget.onDaySelected(index);
setState(() {
_selectedDayIndex = index;
});
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translations.weekTemplateDayTitle, style: textTheme.titleMedium),
const SizedBox(height: 8),
SizedBox(
height: 72,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
for (var day in days) ...[
_DaySelectionCard(
day: day,
days: days,
selectedDayIndex: _selectedDayIndex,
onDaySelected: (selected) =>
onDaySelected(selected, days.indexOf(day)),
),
],
],
),
),
),
],
);
}
}
class _DaySelectionCard extends StatelessWidget {
const _DaySelectionCard({
required this.selectedDayIndex,
required this.day,
required this.days,
required this.onDaySelected,
});
final String day;
final List<String> days;
final int selectedDayIndex;
final void Function(bool) onDaySelected;
@override
Widget build(BuildContext context) {
var index = days.indexOf(day);
var isSelected = index == selectedDayIndex;
return _DaySelectionCardLayout(
day: day,
isSelected: isSelected,
onDaySelected: onDaySelected,
);
}
}
class _DaySelectionCardLayout extends StatelessWidget {
const _DaySelectionCardLayout({
required this.day,
required this.isSelected,
required this.onDaySelected,
});
final String day;
final bool isSelected;
final void Function(bool) onDaySelected;
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var textTheme = theme.textTheme;
var abbreviationTextStyle = textTheme.headlineMedium;
abbreviationTextStyle = isSelected
? abbreviationTextStyle?.copyWith(
color: theme.colorScheme.onPrimary,
)
: abbreviationTextStyle;
return Padding(
padding: const EdgeInsets.only(left: 8.0),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
height: isSelected ? 72 : 64,
width: isSelected ? 72 : 64,
child: ChoiceChip(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
padding: EdgeInsets.zero,
label: Center(
child: Text(
day.toUpperCase(),
style: abbreviationTextStyle,
),
),
selected: isSelected,
showCheckmark: theme.chipTheme.showCheckmark ?? false,
onSelected: onDaySelected,
),
),
);
}
}