fix: use template viewmodels for the UI state

This commit is contained in:
Freek van de Ven 2024-07-16 11:31:38 +02:00 committed by FlutterJoey
parent fd81964b75
commit 313a402409
11 changed files with 387 additions and 169 deletions

View file

@ -1,5 +1,6 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/service/availability_service.dart";
import "package:flutter_availability/src/ui/view_models/break_view_model.dart";
import "package:flutter_availability/src/ui/widgets/availability_clear.dart";
import "package:flutter_availability/src/ui/widgets/availability_template_selection.dart";
import "package:flutter_availability/src/ui/widgets/availabillity_time_selection.dart";
@ -172,12 +173,16 @@ class _AvailabilitiesModificationScreenState
);
var pauseSelection = PauseSelection(
breaks: _availability.breaks,
breaks: _availability.breaks
.map(BreakViewModel.fromAvailabilityBreakModel)
.toList(),
editingTemplate: false,
// TODO(Joey): Extract these
onBreaksChanged: (breaks) {
setState(() {
_availability = _availability.copyWith(breaks: breaks);
_availability = _availability.copyWith(
breaks: breaks.map((b) => b.toBreak()).toList(),
);
});
},
);

View file

@ -1,5 +1,5 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/models/view_template_daydata.dart";
import "package:flutter_availability/src/ui/view_models/day_template_view_model.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";
@ -28,25 +28,16 @@ class DayTemplateModificationScreen extends StatefulWidget {
class _DayTemplateModificationScreenState
extends State<DayTemplateModificationScreen> {
late int? _selectedColor;
late AvailabilityTemplateModel _template;
late DayTemplateViewModel _viewModel;
@override
void initState() {
super.initState();
_selectedColor = widget.template?.color;
_template = widget.template ??
AvailabilityTemplateModel(
userId: "1",
name: "",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime.now(),
endTime: DateTime.now(),
breaks: [],
),
);
if (widget.template != null) {
_viewModel = DayTemplateViewModel.fromTemplate(widget.template!);
} else {
_viewModel = const DayTemplateViewModel();
}
}
@override
@ -59,20 +50,21 @@ class _DayTemplateModificationScreenState
var spacing = options.spacing;
Future<void> onDeletePressed() async {
await service.deleteTemplate(_template);
await service.deleteTemplate(widget.template!);
widget.onExit();
}
Future<void> onSavePressed() async {
var template = _viewModel.toTemplate();
if (widget.template == null) {
await service.createTemplate(_template);
await service.createTemplate(template);
} else {
await service.updateTemplate(_template);
await service.updateTemplate(template);
}
widget.onExit();
}
var canSave = _template.name.isNotEmpty && _selectedColor != null;
var canSave = _viewModel.canSave;
var saveButton = options.primaryButtonBuilder(
context,
@ -94,34 +86,29 @@ class _DayTemplateModificationScreenState
);
var templateTitleSection = TemplateNameInput(
initialValue: _template.name,
initialValue: _viewModel.name,
onNameChanged: (name) {
setState(() {
_template = _template.copyWith(name: name);
_viewModel = _viewModel.copyWith(name: name);
});
},
);
var colorSection = TemplateColorSelection(
selectedColor: _selectedColor,
selectedColor: _viewModel.color,
// TODO(Joey): Extract this
onColorSelected: (color) {
setState(() {
_selectedColor = color;
_template = _template.copyWith(color: color);
_viewModel = _viewModel.copyWith(color: color);
});
},
);
var availabilitySection = TemplateTimeAndBreakSection(
dayData: ViewDayTemplateData.fromDayTemplateData(
_template.templateData as DayTemplateData,
),
dayData: _viewModel.data,
onDayDataChanged: (data) {
setState(() {
_template = _template.copyWith(
templateData: data.toDayTemplateData(),
);
_viewModel = _viewModel.copyWith(data: data);
});
},
);

View file

@ -1,5 +1,6 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/models/view_template_daydata.dart";
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
import "package:flutter_availability/src/ui/view_models/week_template_view_models.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";
@ -30,23 +31,16 @@ class WeekTemplateModificationScreen extends StatefulWidget {
class _WeekTemplateModificationScreenState
extends State<WeekTemplateModificationScreen> {
late int? _selectedColor;
late AvailabilityTemplateModel _template;
bool _editing = true;
WeekDay _selectedDay = WeekDay.monday;
late WeekTemplateViewModel _viewModel;
@override
void initState() {
super.initState();
_selectedColor = widget.template?.color;
_template = widget.template ??
AvailabilityTemplateModel(
userId: "1",
name: "",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(),
);
_viewModel = widget.template != null
? WeekTemplateViewModel.fromTemplate(widget.template!)
: const WeekTemplateViewModel();
}
@override
@ -59,11 +53,31 @@ class _WeekTemplateModificationScreenState
var translations = options.translations;
var spacing = options.spacing;
var weekTemplateDate = _template.templateData as WeekTemplateData;
var selectedDayData = weekTemplateDate.data[_selectedDay];
var weekTemplateDate = _viewModel.data;
var selectedDayData = weekTemplateDate[_selectedDay];
void onDayDataChanged(DayTemplateDataViewModel data) {
setState(() {
// create a new copy of an unmodifiable map that can be modified
var updatedDays =
Map<WeekDay, DayTemplateDataViewModel>.from(weekTemplateDate);
if (data.isEmpty) {
updatedDays.remove(_selectedDay);
} else {
updatedDays[_selectedDay] = data;
}
_viewModel = _viewModel.copyWith(data: updatedDays);
});
}
void onDaySelected(int day) {
setState(() {
_selectedDay = WeekDay.values[day];
});
}
Future<void> onDeletePressed() async {
await service.deleteTemplate(_template);
await service.deleteTemplate(widget.template!);
widget.onExit();
}
@ -84,15 +98,20 @@ class _WeekTemplateModificationScreenState
}
Future<void> onSavePressed() async {
if (!_viewModel.isValid) {
// TODO(freek): show error message
return;
}
var template = _viewModel.toTemplate();
if (widget.template == null) {
await service.createTemplate(_template);
await service.createTemplate(template);
} else {
await service.updateTemplate(_template);
await service.updateTemplate(template);
}
widget.onExit();
}
var canSave = _template.name.isNotEmpty && _selectedColor != null;
var canSave = _viewModel.canSave;
var nextButton = options.primaryButtonBuilder(
context,
canSave ? onNextPressed : null,
@ -125,20 +144,19 @@ class _WeekTemplateModificationScreenState
);
var templateTitleSection = TemplateNameInput(
initialValue: _template.name,
initialValue: _viewModel.name,
onNameChanged: (name) {
setState(() {
_template = _template.copyWith(name: name);
_viewModel = _viewModel.copyWith(name: name);
});
},
);
var colorSection = TemplateColorSelection(
selectedColor: _selectedColor,
selectedColor: _viewModel.color,
onColorSelected: (color) {
setState(() {
_selectedColor = color;
_template = _template.copyWith(color: color);
_viewModel = _viewModel.copyWith(color: color);
});
},
);
@ -148,37 +166,13 @@ class _WeekTemplateModificationScreenState
const SizedBox(height: 24),
Padding(
padding: EdgeInsets.only(left: spacing.sidePadding),
child: TemplateWeekDaySelection(
onDaySelected: (day) {
setState(() {
_selectedDay = WeekDay.values[day];
});
},
),
child: TemplateWeekDaySelection(onDaySelected: onDaySelected),
),
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(),
},
),
);
});
},
dayData: selectedDayData ?? const DayTemplateDataViewModel(),
onDayDataChanged: onDayDataChanged,
),
),
const SizedBox(height: 24),
@ -203,20 +197,20 @@ class _WeekTemplateModificationScreenState
children: [
Container(
decoration: BoxDecoration(
color: Color(_template.color),
color: Color(_viewModel.color ?? 0),
borderRadius: BorderRadius.circular(5),
),
width: 20,
height: 20,
),
const SizedBox(width: 12),
Text(_template.name, style: textTheme.bodyLarge),
Text(_viewModel.name ?? "", style: textTheme.bodyLarge),
],
),
),
const SizedBox(height: 30),
TemplateWeekOverview(
template: _template,
template: _viewModel,
),
],
),

View file

@ -0,0 +1,87 @@
import "package:flutter/material.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
/// View model to represent the data during the modification of a break
class BreakViewModel {
/// Constructor
const BreakViewModel({
this.startTime,
this.endTime,
this.duration,
});
/// Create a [BreakViewModel] from a [AvailabilityBreakModel]
factory BreakViewModel.fromAvailabilityBreakModel(
AvailabilityBreakModel data,
) =>
BreakViewModel(
startTime: TimeOfDay.fromDateTime(data.startTime),
endTime: TimeOfDay.fromDateTime(data.endTime),
duration: data.duration,
);
/// The start time for this break
final TimeOfDay? startTime;
/// The end time for this break
final TimeOfDay? endTime;
/// The full duration of the actual break.
///
/// This is allowed to diverge from the difference between [startTime] and
/// [endTime] to indicate that the break is somewhere between [startTime] and
/// [endTime]
final Duration? duration;
/// Returns true if the break is valid
/// The start is before the end and the duration is equal or lower than the
/// difference between the start and end
bool get isValid {
if (startTime == null || endTime == null) {
return false;
}
var startDateTime = DateTime(0, 1, 1, startTime!.hour, startTime!.minute);
var endDateTime = DateTime(0, 1, 1, endTime!.hour, endTime!.minute);
if (startDateTime.isAfter(endDateTime)) {
return false;
}
if (duration == null) {
return true;
}
var actualDuration = endDateTime.difference(startDateTime);
return duration! <= actualDuration;
}
/// Whether the save/next button should be enabled
bool get canSave => startTime != null && endTime != null;
/// Convert to [AvailabilityBreakModel] for saving
AvailabilityBreakModel toBreak() => AvailabilityBreakModel(
startTime: DateTime(0, 0, 0, startTime!.hour, startTime!.minute),
endTime: DateTime(0, 0, 0, endTime!.hour, endTime!.minute),
duration: duration,
);
/// Create a copy with new values
BreakViewModel copyWith({
TimeOfDay? startTime,
TimeOfDay? endTime,
Duration? duration,
}) =>
BreakViewModel(
startTime: startTime ?? this.startTime,
endTime: endTime ?? this.endTime,
duration: duration ?? this.duration,
);
/// compareto method to order two breaks based on their start time
/// multiples hours are converted to minutes and added to the minutes
int compareTo(BreakViewModel other) {
var difference = startTime!.hour * 60 +
startTime!.minute -
other.startTime!.hour * 60 -
other.startTime!.minute;
return difference;
}
}

View file

@ -0,0 +1,69 @@
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
/// View model to represent the data during the modification of a day template
class DayTemplateViewModel {
/// Constructor
const DayTemplateViewModel({
this.data = const DayTemplateDataViewModel(),
this.id,
this.name,
this.color,
});
/// Create a [WeekTemplateViewModel] from a [AvailabilityTemplateModel]
factory DayTemplateViewModel.fromTemplate(
AvailabilityTemplateModel template,
) {
var data = template.templateData as DayTemplateData;
return DayTemplateViewModel(
id: template.id,
name: template.name,
color: template.color,
data: DayTemplateDataViewModel.fromDayTemplateData(data),
);
}
/// The identifier for this template
final String? id;
/// The name by which the template can be visually identified
final String? name;
/// The color by which the template can be visually identified
final int? color;
/// The data for the template day
final DayTemplateDataViewModel data;
/// Whether the data is valid for saving
bool get isValid => data.isValid;
/// Whether the save/next button should be enabled
bool get canSave =>
color != null && (name?.isNotEmpty ?? false) && data.canSave;
/// Convert to [AvailabilityTemplateModel] for saving
AvailabilityTemplateModel toTemplate() => AvailabilityTemplateModel(
id: id,
userId: "",
name: name!,
color: color!,
templateType: AvailabilityTemplateType.day,
templateData: data.toDayTemplateData(),
);
/// Create a copy with new values
DayTemplateViewModel copyWith({
String? id,
String? name,
int? color,
DayTemplateDataViewModel? data,
}) =>
DayTemplateViewModel(
id: id ?? this.id,
name: name ?? this.name,
color: color ?? this.color,
data: data ?? this.data,
);
}

View file

@ -1,21 +1,23 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/view_models/break_view_model.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
/// The data for creating or editing a day template
class ViewDayTemplateData {
class DayTemplateDataViewModel {
/// Constructor
const ViewDayTemplateData({
const DayTemplateDataViewModel({
this.startTime,
this.endTime,
this.breaks = const [],
});
/// Create a new instance from a [DayTemplateData]
factory ViewDayTemplateData.fromDayTemplateData(DayTemplateData data) =>
ViewDayTemplateData(
factory DayTemplateDataViewModel.fromDayTemplateData(DayTemplateData data) =>
DayTemplateDataViewModel(
startTime: TimeOfDay.fromDateTime(data.startTime),
endTime: TimeOfDay.fromDateTime(data.endTime),
breaks: data.breaks,
breaks:
data.breaks.map(BreakViewModel.fromAvailabilityBreakModel).toList(),
);
/// The start time to apply on a new availability
@ -25,18 +27,28 @@ class ViewDayTemplateData {
final TimeOfDay? endTime;
/// A list of breaks to apply to every new availability
final List<AvailabilityBreakModel> breaks;
final List<BreakViewModel> breaks;
/// Whether the data is valid for saving
bool get isValid => startTime != null && endTime != null;
/// Whether the data is valid
/// The start is before the end
/// There are no breaks that are invalid
/// The breaks are not outside the start and end time
/// The breaks are not overlapping
bool get isValid => canSave && breaks.every((b) => b.isValid);
/// Whether the save/next button should be enabled
bool get canSave => startTime != null && endTime != null;
/// Whether the day is empty and can be removed from the template when saving
bool get isEmpty => startTime == null && endTime == null && breaks.isEmpty;
/// Create a copy with new values
ViewDayTemplateData copyWith({
DayTemplateDataViewModel copyWith({
TimeOfDay? startTime,
TimeOfDay? endTime,
List<AvailabilityBreakModel>? breaks,
List<BreakViewModel>? breaks,
}) =>
ViewDayTemplateData(
DayTemplateDataViewModel(
startTime: startTime ?? this.startTime,
endTime: endTime ?? this.endTime,
breaks: breaks ?? this.breaks,
@ -47,6 +59,6 @@ class ViewDayTemplateData {
startTime:
DateTime(0, 0, 0, startTime?.hour ?? 0, startTime?.minute ?? 0),
endTime: DateTime(0, 0, 0, endTime?.hour ?? 0, endTime?.minute ?? 0),
breaks: breaks,
breaks: breaks.map((b) => b.toBreak()).toList(),
);
}

View file

@ -0,0 +1,83 @@
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
/// View model to represent the data during the modification of a week template
class WeekTemplateViewModel {
/// Constructor
const WeekTemplateViewModel({
this.data = const {},
this.id,
this.name,
this.color,
});
/// Create a [WeekTemplateViewModel] from a [AvailabilityTemplateModel]
factory WeekTemplateViewModel.fromTemplate(
AvailabilityTemplateModel template,
) {
var data = template.templateData as WeekTemplateData;
return WeekTemplateViewModel(
id: template.id,
name: template.name,
color: template.color,
data: {
for (var day in WeekDay.values)
day: data.data.containsKey(day)
? DayTemplateDataViewModel.fromDayTemplateData(
data.data[day]!,
)
: const DayTemplateDataViewModel(),
},
);
}
/// The identifier for this template
final String? id;
/// The name by which the template can be visually identified
final String? name;
/// The color by which the template can be visually identified
final int? color;
/// The data for each day of the week
final Map<WeekDay, DayTemplateDataViewModel> data;
/// Whether the data is valid for saving
/// All days must be valid and there must be at least one day with data
bool get isValid =>
data.values.every((e) => e.isValid) && data.values.isNotEmpty;
/// Whether the save/next button should be enabled
bool get canSave =>
color != null && (name?.isNotEmpty ?? false) && data.values.isNotEmpty;
/// Convert to [AvailabilityTemplateModel] for saving
AvailabilityTemplateModel toTemplate() => AvailabilityTemplateModel(
id: id,
userId: "",
name: name!,
color: color!,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData(
data: {
for (var entry in data.entries)
entry.key: entry.value.toDayTemplateData(),
},
),
);
/// Create a copy with new values
WeekTemplateViewModel copyWith({
String? id,
String? name,
int? color,
Map<WeekDay, DayTemplateDataViewModel>? data,
}) =>
WeekTemplateViewModel(
id: id ?? this.id,
name: name ?? this.name,
color: color ?? this.color,
data: data ?? this.data,
);
}

View file

@ -1,10 +1,10 @@
import "package:flutter/material.dart";
import "package:flutter_availability/flutter_availability.dart";
import "package:flutter_availability/src/service/availability_service.dart";
import "package:flutter_availability/src/ui/view_models/break_view_model.dart";
import "package:flutter_availability/src/ui/widgets/generic_time_selection.dart";
import "package:flutter_availability/src/ui/widgets/input_fields.dart";
import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
///
class PauseSelection extends StatelessWidget {
@ -17,10 +17,10 @@ class PauseSelection extends StatelessWidget {
});
/// The breaks that are currently set
final List<AvailabilityBreakModel> breaks;
final List<BreakViewModel> breaks;
/// Callback for when the breaks are changed
final void Function(List<AvailabilityBreakModel>) onBreaksChanged;
final void Function(List<BreakViewModel>) onBreaksChanged;
/// Whether the pause selection is used for editing a template
final bool editingTemplate;
@ -33,11 +33,12 @@ class PauseSelection extends StatelessWidget {
var options = availabilityScope.options;
var translations = options.translations;
Future<AvailabilityBreakModel?> openBreakDialog(
AvailabilityBreakModel? initialBreak,
Future<BreakViewModel?> openBreakDialog(
BreakViewModel? initialBreak,
) async =>
AvailabilityBreakSelectionDialog.show(
context,
initialBreak: initialBreak,
userId: availabilityScope.userId,
options: options,
service: availabilityScope.service,
@ -52,7 +53,7 @@ class PauseSelection extends StatelessWidget {
onBreaksChanged(updatedBreaks);
}
Future<void> onEditBreak(AvailabilityBreakModel availabilityBreak) async {
Future<void> onEditBreak(BreakViewModel availabilityBreak) async {
var updatedBreak = await openBreakDialog(availabilityBreak);
if (updatedBreak == null) return;
@ -60,13 +61,12 @@ class PauseSelection extends StatelessWidget {
onBreaksChanged(updatedBreaks);
}
void onDeleteBreak(AvailabilityBreakModel availabilityBreak) {
void onDeleteBreak(BreakViewModel availabilityBreak) {
var updatedBreaks = breaks.where((b) => b != availabilityBreak).toList();
onBreaksChanged(updatedBreaks);
}
var sortedBreaks = breaks.toList()
..sort((a, b) => a.startTime.compareTo(b.startTime));
var sortedBreaks = breaks.toList()..sort((a, b) => a.compareTo(b));
var addButton = options.bigTextButtonWrapperBuilder(
context,
@ -120,7 +120,7 @@ class BreakDisplay extends StatelessWidget {
});
/// The break to display
final AvailabilityBreakModel breakModel;
final BreakViewModel breakModel;
/// Callback for when the minus button is clicked
final VoidCallback onRemove;
@ -138,11 +138,11 @@ class BreakDisplay extends StatelessWidget {
var starTime = translations.timeFormatter(
context,
TimeOfDay.fromDateTime(breakModel.startTime),
breakModel.startTime!,
);
var endTime = translations.timeFormatter(
context,
TimeOfDay.fromDateTime(breakModel.endTime),
breakModel.endTime!,
);
// TODO(Joey): Watch out with gesture detectors
@ -158,7 +158,7 @@ class BreakDisplay extends StatelessWidget {
child: Row(
children: [
Text(
"${breakModel.duration.inMinutes} "
"${breakModel.duration!.inMinutes} "
"${translations.timeMinutes} | "
"$starTime - "
"$endTime",
@ -183,22 +183,22 @@ class AvailabilityBreakSelectionDialog extends StatefulWidget {
});
/// The initial break to show in the dialog if any
final AvailabilityBreakModel? initialBreak;
final BreakViewModel? initialBreak;
/// Whether the dialog is used to edit a template
/// This will change the description of the dialog
final bool editingTemplate;
/// Opens the dialog to add a break
static Future<AvailabilityBreakModel?> show(
static Future<BreakViewModel?> show(
BuildContext context, {
required AvailabilityOptions options,
required String userId,
required AvailabilityService service,
required bool editingTemplate,
AvailabilityBreakModel? initialBreak,
BreakViewModel? initialBreak,
}) async =>
showModalBottomSheet<AvailabilityBreakModel>(
showModalBottomSheet<BreakViewModel>(
context: context,
useSafeArea: false,
shape: const RoundedRectangleBorder(
@ -225,20 +225,12 @@ class AvailabilityBreakSelectionDialog extends StatefulWidget {
class _AvailabilityBreakSelectionDialogState
extends State<AvailabilityBreakSelectionDialog> {
late TimeOfDay? _startTime;
late TimeOfDay? _endTime;
late Duration? _duration;
late BreakViewModel _breakViewModel;
@override
void initState() {
super.initState();
_startTime = widget.initialBreak != null
? TimeOfDay.fromDateTime(widget.initialBreak!.startTime)
: null;
_endTime = widget.initialBreak != null
? TimeOfDay.fromDateTime(widget.initialBreak!.endTime)
: null;
_duration = widget.initialBreak?.duration;
_breakViewModel = widget.initialBreak ?? const BreakViewModel();
}
@override
@ -252,44 +244,31 @@ class _AvailabilityBreakSelectionDialogState
void onUpdateDuration(Duration duration) {
setState(() {
_duration = duration;
_breakViewModel = _breakViewModel.copyWith(duration: duration);
});
}
void onUpdateStart(TimeOfDay start) {
setState(() {
_startTime = start;
_breakViewModel = _breakViewModel.copyWith(startTime: start);
});
}
void onUpdateEnd(TimeOfDay end) {
setState(() {
_endTime = end;
_breakViewModel = _breakViewModel.copyWith(endTime: end);
});
}
var canSave = _startTime != null && _endTime != null;
var canSave = _breakViewModel.canSave;
var onSaveButtonPress = canSave
? () {
var breakModel = AvailabilityBreakModel(
startTime: DateTime(
DateTime.now().year,
DateTime.now().month,
DateTime.now().day,
_startTime!.hour,
_startTime!.minute,
),
endTime: DateTime(
DateTime.now().year,
DateTime.now().month,
DateTime.now().day,
_endTime!.hour,
_endTime!.minute,
),
duration: _duration,
);
Navigator.of(context).pop(breakModel);
if (_breakViewModel.isValid) {
Navigator.of(context).pop(_breakViewModel);
}
debugPrint("Break is not valid");
// TODO(freek): show error message
}
: null;
@ -333,7 +312,7 @@ class _AvailabilityBreakSelectionDialogState
Expanded(
flex: 2,
child: DurationInputField(
initialValue: _duration,
initialValue: _breakViewModel.duration,
onDurationChanged: onUpdateDuration,
),
),
@ -342,13 +321,15 @@ class _AvailabilityBreakSelectionDialogState
),
const SizedBox(height: 24),
TimeSelection(
key: ValueKey(
[_breakViewModel.startTime, _breakViewModel.endTime],
),
// rebuild the widget when the start or end time changes
key: ValueKey([_startTime, _endTime]),
title: translations.pauseDialogPeriodTitle,
description: translations.pauseDialogPeriodDescription,
crossAxisAlignment: CrossAxisAlignment.center,
startTime: _startTime,
endTime: _endTime,
startTime: _breakViewModel.startTime,
endTime: _breakViewModel.endTime,
onStartChanged: onUpdateStart,
onEndChanged: onUpdateEnd,
),

View file

@ -1,5 +1,5 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/models/view_template_daydata.dart";
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
import "package:flutter_availability/src/ui/widgets/pause_selection.dart";
import "package:flutter_availability/src/ui/widgets/template_time_selection.dart";
@ -8,15 +8,15 @@ class TemplateTimeAndBreakSection extends StatelessWidget {
///
const TemplateTimeAndBreakSection({
required this.onDayDataChanged,
this.dayData = const ViewDayTemplateData(),
this.dayData = const DayTemplateDataViewModel(),
super.key,
});
/// The day data to display and edit
final ViewDayTemplateData dayData;
final DayTemplateDataViewModel dayData;
/// Callback for when the day data changes
final void Function(ViewDayTemplateData data) onDayDataChanged;
final void Function(DayTemplateDataViewModel data) onDayDataChanged;
@override
Widget build(BuildContext context) => Column(

View file

@ -1,5 +1,7 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/models/view_template_daydata.dart";
import "package:flutter_availability/src/ui/view_models/break_view_model.dart";
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
import "package:flutter_availability/src/ui/view_models/week_template_view_models.dart";
import "package:flutter_availability/src/ui/widgets/calendar_grid.dart";
import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
@ -13,7 +15,7 @@ class TemplateWeekOverview extends StatelessWidget {
});
/// The template to show
final AvailabilityTemplateModel template;
final WeekTemplateViewModel template;
@override
Widget build(BuildContext context) {
@ -26,7 +28,7 @@ class TemplateWeekOverview extends StatelessWidget {
var dayNames = getDaysOfTheWeekAsStrings(translations, context);
var templateData = template.templateData as WeekTemplateData;
var templateData = template.data;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -50,11 +52,8 @@ class TemplateWeekOverview extends StatelessWidget {
for (var day in WeekDay.values) ...[
_TemplateDayDetailRow(
dayName: dayNames[day.index],
dayData: templateData.data.containsKey(day)
? ViewDayTemplateData.fromDayTemplateData(
templateData.data[day]!,
)
: null,
dayData:
templateData.containsKey(day) ? templateData[day] : null,
isOdd: day.index.isOdd,
),
],
@ -81,7 +80,7 @@ class _TemplateDayDetailRow extends StatelessWidget {
final bool isOdd;
/// The data of the day
final ViewDayTemplateData? dayData;
final DayTemplateDataViewModel? dayData;
@override
Widget build(BuildContext context) {
@ -102,7 +101,7 @@ class _TemplateDayDetailRow extends StatelessWidget {
dayPeriod = translations.unavailable;
}
var breaks = dayData?.breaks ?? <AvailabilityBreakModel>[];
var breaks = dayData?.breaks ?? <BreakViewModel>[];
BoxDecoration? boxDecoration;
if (isOdd) {
@ -134,7 +133,7 @@ class _TemplateDayDetailRow extends StatelessWidget {
// for each break add a line
for (var dayBreak in breaks) ...[
const SizedBox(height: 4),
_TemplateDayDetailPauseRow(dayBreak: dayBreak),
_TemplateDayDetailPauseRow(dayBreakViewModel: dayBreak),
],
],
),
@ -144,10 +143,10 @@ class _TemplateDayDetailRow extends StatelessWidget {
class _TemplateDayDetailPauseRow extends StatelessWidget {
const _TemplateDayDetailPauseRow({
required this.dayBreak,
required this.dayBreakViewModel,
});
final AvailabilityBreakModel dayBreak;
final BreakViewModel dayBreakViewModel;
@override
Widget build(BuildContext context) {
@ -157,6 +156,7 @@ class _TemplateDayDetailPauseRow extends StatelessWidget {
var options = availabilityScope.options;
var translations = options.translations;
var dayBreak = dayBreakViewModel.toBreak();
var startTime = TimeOfDay.fromDateTime(dayBreak.startTime);
var endTime = TimeOfDay.fromDateTime(dayBreak.endTime);
var startTimeString = translations.timeFormatter(context, startTime);