feat: add week template modification screen

This commit is contained in:
Freek van de Ven 2024-07-16 10:18:08 +02:00 committed by FlutterJoey
parent c7ea82677c
commit fd81964b75
5 changed files with 341 additions and 14 deletions

View file

@ -37,6 +37,7 @@ class AvailabilityTranslations {
required this.createDayTemplate,
required this.createWeekTemplate,
required this.deleteTemplateButton,
required this.editTemplateButton,
required this.dayTemplateTitle,
required this.weekTemplateTitle,
required this.weekTemplateDayTitle,
@ -60,6 +61,7 @@ class AvailabilityTranslations {
required this.pauseDialogPeriodDescription,
required this.saveButton,
required this.addButton,
required this.nextButton,
required this.confirmText,
required this.cancelText,
required this.timeFormatter,
@ -103,6 +105,7 @@ class AvailabilityTranslations {
this.createDayTemplate = "Create day template",
this.createWeekTemplate = "Create week template",
this.deleteTemplateButton = "Delete template",
this.editTemplateButton = "Edit template",
this.dayTemplateTitle = "Day template",
this.weekTemplateTitle = "Week template",
this.weekTemplateDayTitle = "When",
@ -130,6 +133,7 @@ class AvailabilityTranslations {
"Select between which times you want to take a break",
this.saveButton = "Save",
this.addButton = "Add",
this.nextButton = "Next",
this.confirmText = "Yes",
this.cancelText = "No",
this.dayMonthFormatter = _defaultDayMonthFormatter,
@ -219,6 +223,9 @@ class AvailabilityTranslations {
/// The label on the button to delete a template
final String deleteTemplateButton;
/// The label on the button to edit a template
final String editTemplateButton;
/// The title for the day template edit screen
final String dayTemplateTitle;
@ -291,6 +298,9 @@ class AvailabilityTranslations {
/// The text on the add button
final String addButton;
/// The text on the next button
final String nextButton;
/// The text on the confirm button in the confirmation dialog
final String confirmText;

View file

@ -4,6 +4,7 @@ import "package:flutter_availability/src/service/availability_service.dart";
import "package:flutter_availability/src/ui/screens/availability_modification.dart";
import "package:flutter_availability/src/ui/screens/template_day_modification.dart";
import "package:flutter_availability/src/ui/screens/template_overview.dart";
import "package:flutter_availability/src/ui/screens/template_week_modification.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
///
@ -17,6 +18,17 @@ MaterialPageRoute homePageRoute(VoidCallback onExit) => MaterialPageRoute(
),
);
Future _routeToTemplateEditScreen(
BuildContext context,
AvailabilityTemplateModel template,
) async {
if (template.templateType == AvailabilityTemplateType.day) {
await Navigator.of(context).push(templateEditDayRoute(template));
} else if (template.templateType == AvailabilityTemplateType.week) {
await Navigator.of(context).push(templateEditWeekRoute(template));
}
}
///
MaterialPageRoute<AvailabilityTemplateModel?> templateOverviewRoute({
bool allowSelection = false,
@ -24,21 +36,22 @@ MaterialPageRoute<AvailabilityTemplateModel?> templateOverviewRoute({
MaterialPageRoute(
builder: (context) => AvailabilityTemplateOverview(
onExit: () => Navigator.of(context).pop(),
onEditTemplate: (template) async {
if (template.templateType == AvailabilityTemplateType.day) {
await Navigator.of(context).push(templateEditDayRoute(template));
}
},
onEditTemplate: (template) async =>
_routeToTemplateEditScreen(context, template),
onAddTemplate: (type) async {
if (type == AvailabilityTemplateType.day) {
await Navigator.of(context).push(templateEditDayRoute(null));
} else if (type == AvailabilityTemplateType.week) {
await Navigator.of(context).push(templateEditWeekRoute(null));
}
},
onSelectTemplate: (template) async {
if (allowSelection) {
Navigator.of(context).pop(template);
} else {
await _routeToTemplateEditScreen(context, template);
}
},
onSelectTemplate: allowSelection
? (template) async {
Navigator.of(context).pop(template);
}
: null,
),
);
@ -51,6 +64,15 @@ MaterialPageRoute templateEditDayRoute(AvailabilityTemplateModel? template) =>
),
);
///
MaterialPageRoute templateEditWeekRoute(AvailabilityTemplateModel? template) =>
MaterialPageRoute(
builder: (context) => WeekTemplateModificationScreen(
template: template,
onExit: () => Navigator.of(context).pop(),
),
);
///
MaterialPageRoute availabilityViewRoute(
DateTimeRange dateRange,

View file

@ -0,0 +1,296 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/models/view_template_daydata.dart";
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
import "package:flutter_availability/src/ui/widgets/template_name_input.dart";
import "package:flutter_availability/src/ui/widgets/template_time_break.dart";
import "package:flutter_availability/src/ui/widgets/template_week_day_selection.dart";
import "package:flutter_availability/src/ui/widgets/template_week_overview.dart";
import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
/// Page for creating or editing a day template
class WeekTemplateModificationScreen extends StatefulWidget {
/// Constructor
const WeekTemplateModificationScreen({
required this.template,
required this.onExit,
super.key,
});
/// The week template to edit or null if creating a new one
final AvailabilityTemplateModel? template;
/// Callback for when the user wants to navigate back
final VoidCallback onExit;
@override
State<WeekTemplateModificationScreen> createState() =>
_WeekTemplateModificationScreenState();
}
class _WeekTemplateModificationScreenState
extends State<WeekTemplateModificationScreen> {
late int? _selectedColor;
late AvailabilityTemplateModel _template;
bool _editing = true;
WeekDay _selectedDay = WeekDay.monday;
@override
void initState() {
super.initState();
_selectedColor = widget.template?.color;
_template = widget.template ??
AvailabilityTemplateModel(
userId: "1",
name: "",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(),
);
}
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var textTheme = theme.textTheme;
var availabilityScope = AvailabilityScope.of(context);
var service = availabilityScope.service;
var options = availabilityScope.options;
var translations = options.translations;
var spacing = options.spacing;
var weekTemplateDate = _template.templateData as WeekTemplateData;
var selectedDayData = weekTemplateDate.data[_selectedDay];
Future<void> onDeletePressed() async {
await service.deleteTemplate(_template);
widget.onExit();
}
void onNextPressed() {
setState(() {
_editing = false;
});
}
void onBackPressed() {
if (_editing) {
widget.onExit();
} else {
setState(() {
_editing = true;
});
}
}
Future<void> onSavePressed() async {
if (widget.template == null) {
await service.createTemplate(_template);
} else {
await service.updateTemplate(_template);
}
widget.onExit();
}
var canSave = _template.name.isNotEmpty && _selectedColor != null;
var nextButton = options.primaryButtonBuilder(
context,
canSave ? onNextPressed : null,
Text(translations.nextButton),
);
var saveButton = options.primaryButtonBuilder(
context,
canSave ? onSavePressed : null,
Text(translations.saveButton),
);
var deleteButton = options.bigTextButtonBuilder(
context,
onDeletePressed,
Text(translations.deleteTemplateButton),
);
var previousButton = options.bigTextButtonBuilder(
context,
onBackPressed,
Text(translations.editTemplateButton),
);
var title = Center(
child: Text(
translations.weekTemplateTitle,
style: theme.textTheme.displaySmall,
),
);
var templateTitleSection = TemplateNameInput(
initialValue: _template.name,
onNameChanged: (name) {
setState(() {
_template = _template.copyWith(name: name);
});
},
);
var colorSection = TemplateColorSelection(
selectedColor: _selectedColor,
onColorSelected: (color) {
setState(() {
_selectedColor = color;
_template = _template.copyWith(color: color);
});
},
);
var editPage = [
_WeekTemplateSidePadding(child: templateTitleSection),
const SizedBox(height: 24),
Padding(
padding: EdgeInsets.only(left: spacing.sidePadding),
child: TemplateWeekDaySelection(
onDaySelected: (day) {
setState(() {
_selectedDay = WeekDay.values[day];
});
},
),
),
const SizedBox(height: 24),
_WeekTemplateSidePadding(
child: TemplateTimeAndBreakSection(
dayData: selectedDayData != null
? ViewDayTemplateData.fromDayTemplateData(
selectedDayData,
)
: const ViewDayTemplateData(),
onDayDataChanged: (data) {
setState(() {
_template = _template.copyWith(
templateData:
// create a copy of the week template data
WeekTemplateData(
data: {
for (var entry in weekTemplateDate.data.entries)
entry.key: entry.value,
_selectedDay: data.toDayTemplateData(),
},
),
);
});
},
),
),
const SizedBox(height: 24),
_WeekTemplateSidePadding(child: colorSection),
];
var overviewPage = _WeekTemplateSidePadding(
child: Column(
children: [
Text(translations.templateTitleLabel, style: textTheme.titleMedium),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(
color: theme.colorScheme.primary,
width: 1,
),
borderRadius: BorderRadius.circular(5),
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Color(_template.color),
borderRadius: BorderRadius.circular(5),
),
width: 20,
height: 20,
),
const SizedBox(width: 12),
Text(_template.name, style: textTheme.bodyLarge),
],
),
),
const SizedBox(height: 30),
TemplateWeekOverview(
template: _template,
),
],
),
);
var body = CustomScrollView(
slivers: [
SliverList.list(
children: [
const SizedBox(height: 40),
_WeekTemplateSidePadding(child: title),
const SizedBox(height: 24),
if (_editing) ...[
...editPage,
] else ...[
overviewPage,
],
const SizedBox(height: 32),
],
),
SliverFillRemaining(
fillOverscroll: false,
hasScrollBody: false,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: spacing.sidePadding,
).copyWith(
bottom: spacing.bottomButtonPadding,
),
child: Align(
alignment: Alignment.bottomCenter,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (_editing) ...[
nextButton,
] else ...[
saveButton,
const SizedBox(height: 8),
previousButton,
],
if (widget.template != null) ...[
const SizedBox(height: 8),
deleteButton,
],
],
),
),
),
),
],
);
return options.baseScreenBuilder(context, onBackPressed, body);
}
}
/// One of the elements in the week template modification screen doesn't have a
/// padding around it that is why all the other elements are wrapped in
/// [_WeekTemplateSidePadding]
class _WeekTemplateSidePadding extends StatelessWidget {
const _WeekTemplateSidePadding({
required this.child,
});
final Widget child;
@override
Widget build(BuildContext context) {
var availabilityScope = AvailabilityScope.of(context);
var sidePadding = availabilityScope.options.spacing.sidePadding;
return Padding(
padding: EdgeInsets.symmetric(horizontal: sidePadding),
child: child,
);
}
}

View file

@ -37,8 +37,7 @@ class CalendarGrid extends StatelessWidget {
var calendarDays =
_generateCalendarDays(month, days, selectedRange, colors, colorScheme);
var dayNames =
getDaysOfTheWeekAsAbbreviatedStrings(translations, context);
var dayNames = getDaysOfTheWeekAsAbbreviatedStrings(translations, context);
var calendarDaysRow = Row(
children: List.generate(13, (index) {