mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-18 20:53:45 +02:00
feat: add day template edit/creation page
This commit is contained in:
parent
f2e279a592
commit
39da1a4f21
10 changed files with 594 additions and 4 deletions
|
@ -16,6 +16,7 @@ class AvailabilityOptions {
|
|||
this.primaryButtonBuilder = DefaultPrimaryButton.builder,
|
||||
this.textButtonBuilder = DefaultTextButton.builder,
|
||||
this.spacing = const AvailabilitySpacing(),
|
||||
this.textStyles = const AvailabilityTextStyles(),
|
||||
this.colors = const AvailabilityColors(),
|
||||
AvailabilityDataInterface? dataInterface,
|
||||
}) : dataInterface = dataInterface ?? LocalAvailabilityDataInterface();
|
||||
|
@ -42,6 +43,9 @@ class AvailabilityOptions {
|
|||
/// The spacing between elements
|
||||
final AvailabilitySpacing spacing;
|
||||
|
||||
/// The configurable text styles in the userstory
|
||||
final AvailabilityTextStyles textStyles;
|
||||
|
||||
/// The colors used in the userstory
|
||||
final AvailabilityColors colors;
|
||||
}
|
||||
|
@ -61,6 +65,18 @@ class AvailabilitySpacing {
|
|||
final double sidePadding;
|
||||
}
|
||||
|
||||
/// All customizable text styles for the availability userstory
|
||||
/// If text styles are not provided the text styles will be taken from the theme
|
||||
class AvailabilityTextStyles {
|
||||
/// Constructor for the AvailabilityTextStyles
|
||||
const AvailabilityTextStyles({
|
||||
this.inputFieldTextStyle,
|
||||
});
|
||||
|
||||
/// The text style for the filled in text on all the input fields
|
||||
final TextStyle? inputFieldTextStyle;
|
||||
}
|
||||
|
||||
/// Contains all the customizable colors for the availability userstory
|
||||
///
|
||||
/// If colors are not provided the colors will be taken from the theme
|
||||
|
@ -74,7 +90,14 @@ class AvailabilityColors {
|
|||
this.outsideMonthTextColor,
|
||||
this.textDarkColor,
|
||||
this.textLightColor,
|
||||
this.templateColors,
|
||||
this.templateColors = const [
|
||||
Color(0xFF9bb8f2),
|
||||
Color(0xFF4b77d0),
|
||||
Color(0xFF283a5e),
|
||||
Color(0xFF57947d),
|
||||
Color(0xFFef6c75),
|
||||
Color(0xFFb7198b),
|
||||
],
|
||||
});
|
||||
|
||||
/// The color of the text for the days that are not in the current month
|
||||
|
@ -98,7 +121,7 @@ class AvailabilityColors {
|
|||
final Color? textDarkColor;
|
||||
|
||||
/// The colors that are used for the templates
|
||||
final List<Color>? templateColors;
|
||||
final List<Color> templateColors;
|
||||
}
|
||||
|
||||
/// Builder definition for providing a base screen surrounding each page
|
||||
|
|
|
@ -22,6 +22,18 @@ class AvailabilityTranslations {
|
|||
required this.weekTemplates,
|
||||
required this.createDayTemplate,
|
||||
required this.createWeekTemplate,
|
||||
required this.deleteTemplateButton,
|
||||
required this.dayTemplateTitle,
|
||||
required this.templateTitleHintText,
|
||||
required this.templateTitleLabel,
|
||||
required this.templateColorLabel,
|
||||
required this.time,
|
||||
required this.timeSeparator,
|
||||
required this.templateTimeLabel,
|
||||
required this.pauseSectionTitle,
|
||||
required this.saveButton,
|
||||
required this.addButton,
|
||||
required this.timeFormatter,
|
||||
required this.monthYearFormatter,
|
||||
required this.weekDayAbbreviatedFormatter,
|
||||
});
|
||||
|
@ -40,8 +52,20 @@ class AvailabilityTranslations {
|
|||
this.weekTemplates = "Week templates",
|
||||
this.createDayTemplate = "Create day template",
|
||||
this.createWeekTemplate = "Create week template",
|
||||
this.deleteTemplateButton = "Delete template",
|
||||
this.dayTemplateTitle = "Day template",
|
||||
this.templateTitleHintText = "What do you want to call this template?",
|
||||
this.templateTitleLabel = "Template Title",
|
||||
this.templateColorLabel = "Colorlabel",
|
||||
this.time = "Time",
|
||||
this.timeSeparator = "to",
|
||||
this.templateTimeLabel = "When are you available?",
|
||||
this.pauseSectionTitle = "Add a pause (optional)",
|
||||
this.saveButton = "Save",
|
||||
this.addButton = "Add",
|
||||
this.monthYearFormatter = _defaultMonthYearFormatter,
|
||||
this.weekDayAbbreviatedFormatter = _defaultWeekDayAbbreviatedFormatter,
|
||||
this.timeFormatter = _defaultTimeFormatter,
|
||||
});
|
||||
|
||||
/// The title shown above the calendar
|
||||
|
@ -77,6 +101,39 @@ class AvailabilityTranslations {
|
|||
/// The label for the button to create a new week template
|
||||
final String createWeekTemplate;
|
||||
|
||||
/// The label on the button to delete a template
|
||||
final String deleteTemplateButton;
|
||||
|
||||
/// The title for the day template edit screen
|
||||
final String dayTemplateTitle;
|
||||
|
||||
/// The hint text for the template title input field
|
||||
final String templateTitleHintText;
|
||||
|
||||
/// The label for the template title input field
|
||||
final String templateTitleLabel;
|
||||
|
||||
/// The title above the color selection for templates
|
||||
final String templateColorLabel;
|
||||
|
||||
/// The title for time sections
|
||||
final String time;
|
||||
|
||||
/// The text between start and end time
|
||||
final String timeSeparator;
|
||||
|
||||
/// The label for the template time input
|
||||
final String templateTimeLabel;
|
||||
|
||||
/// The title for pause configuration sections
|
||||
final String pauseSectionTitle;
|
||||
|
||||
/// The text on the save button
|
||||
final String saveButton;
|
||||
|
||||
/// The text on the add button
|
||||
final String addButton;
|
||||
|
||||
/// Gets the month and year formatted as a string
|
||||
///
|
||||
/// The default implementation is `MonthName Year` in english
|
||||
|
@ -87,8 +144,16 @@ class AvailabilityTranslations {
|
|||
/// The default implementation is the first 2 letters of
|
||||
/// the weekday in english
|
||||
final String Function(BuildContext, DateTime) weekDayAbbreviatedFormatter;
|
||||
|
||||
/// Get the time formatted as a string
|
||||
///
|
||||
/// The default implementation is `HH:mm`
|
||||
final String Function(BuildContext, DateTime) timeFormatter;
|
||||
}
|
||||
|
||||
String _defaultTimeFormatter(BuildContext context, DateTime date) =>
|
||||
"${date.hour}:${date.minute}";
|
||||
|
||||
String _defaultWeekDayAbbreviatedFormatter(
|
||||
BuildContext context,
|
||||
DateTime date,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/flutter_availability.dart";
|
||||
import "package:flutter_availability/src/ui/screens/template_availability_day_overview.dart";
|
||||
import "package:flutter_availability/src/ui/screens/template_day_edit.dart";
|
||||
import "package:flutter_availability/src/ui/screens/template_overview.dart";
|
||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||
|
||||
///
|
||||
MaterialPageRoute homePageRoute(VoidCallback onExit) => MaterialPageRoute(
|
||||
|
@ -18,8 +20,25 @@ MaterialPageRoute homePageRoute(VoidCallback onExit) => MaterialPageRoute(
|
|||
MaterialPageRoute templateOverviewRoute() => MaterialPageRoute(
|
||||
builder: (context) => AvailabilityTemplateOverview(
|
||||
onExit: () => Navigator.of(context).pop(),
|
||||
onEditTemplate: (template) {},
|
||||
onAddTemplate: (type) {},
|
||||
onEditTemplate: (template) async {
|
||||
if (template.templateType == AvailabilityTemplateType.day) {
|
||||
await Navigator.of(context).push(templateEditDayRoute(template));
|
||||
}
|
||||
},
|
||||
onAddTemplate: (type) async {
|
||||
if (type == AvailabilityTemplateType.day) {
|
||||
await Navigator.of(context).push(templateEditDayRoute(null));
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
///
|
||||
MaterialPageRoute templateEditDayRoute(AvailabilityTemplateModel? template) =>
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AvailabilityDayTemplateEdit(
|
||||
template: template,
|
||||
onExit: () => Navigator.of(context).pop(),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -84,6 +84,31 @@ class AvailabilityService {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Creates a new template
|
||||
Future<void> createTemplate(AvailabilityTemplateModel template) async {
|
||||
await dataInterface.createTemplateForUser(
|
||||
userId,
|
||||
template,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a template
|
||||
Future<void> updateTemplate(AvailabilityTemplateModel template) async {
|
||||
await dataInterface.updateTemplateForUser(
|
||||
userId,
|
||||
template.id!,
|
||||
template,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a template
|
||||
Future<void> deleteTemplate(AvailabilityTemplateModel template) async {
|
||||
await dataInterface.deleteTemplateForUser(
|
||||
userId,
|
||||
template.id!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A combination of availability and template for a single day
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
import "package:flutter/material.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_selection.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 AvailabilityDayTemplateEdit extends StatefulWidget {
|
||||
///
|
||||
const AvailabilityDayTemplateEdit({
|
||||
required this.template,
|
||||
required this.onExit,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// The day 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<AvailabilityDayTemplateEdit> createState() =>
|
||||
_AvailabilityDayTemplateEditState();
|
||||
}
|
||||
|
||||
class _AvailabilityDayTemplateEditState
|
||||
extends State<AvailabilityDayTemplateEdit> {
|
||||
late int? _selectedColor;
|
||||
late AvailabilityTemplateModel _template;
|
||||
|
||||
@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: [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
var availabilityScope = AvailabilityScope.of(context);
|
||||
var service = availabilityScope.service;
|
||||
var options = availabilityScope.options;
|
||||
var translations = options.translations;
|
||||
var spacing = options.spacing;
|
||||
|
||||
Future<void> onDeletePressed() async {
|
||||
await service.deleteTemplate(_template);
|
||||
widget.onExit();
|
||||
}
|
||||
|
||||
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 saveButton = options.primaryButtonBuilder(
|
||||
context,
|
||||
canSave ? onSavePressed : null,
|
||||
Text(translations.saveButton),
|
||||
);
|
||||
|
||||
var deleteButton = options.textButtonBuilder(
|
||||
context,
|
||||
onDeletePressed,
|
||||
Text(translations.deleteTemplateButton),
|
||||
);
|
||||
|
||||
var title = Center(
|
||||
child: Text(
|
||||
translations.dayTemplateTitle,
|
||||
style: theme.textTheme.displaySmall,
|
||||
),
|
||||
);
|
||||
|
||||
var templateTitleSection = TemplateNameInput(
|
||||
initialValue: _template.name,
|
||||
onNameChanged: (name) {
|
||||
setState(() {
|
||||
_template = _template.copyWith(name: name);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
var timeSection = TemplateTimeSelection(
|
||||
key: ValueKey(_template.templateData),
|
||||
startTime: (_template.templateData as DayTemplateData).startTime,
|
||||
endTime: (_template.templateData as DayTemplateData).endTime,
|
||||
onStartChanged: (start) {
|
||||
setState(() {
|
||||
_template = _template.copyWith(
|
||||
templateData: (_template.templateData as DayTemplateData).copyWith(
|
||||
startTime: start,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
onEndChanged: (end) {
|
||||
setState(() {
|
||||
_template = _template.copyWith(
|
||||
templateData: (_template.templateData as DayTemplateData).copyWith(
|
||||
endTime: end,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
var colorSection = TemplateColorSelection(
|
||||
selectedColor: _selectedColor,
|
||||
onColorSelected: (color) {
|
||||
setState(() {
|
||||
_selectedColor = color;
|
||||
_template = _template.copyWith(color: color);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
var pauseSection = const SizedBox(
|
||||
height: 200,
|
||||
child: Placeholder(),
|
||||
);
|
||||
|
||||
var body = CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: spacing.sidePadding),
|
||||
sliver: SliverList.list(
|
||||
children: [
|
||||
const SizedBox(height: 40),
|
||||
title,
|
||||
const SizedBox(height: 24),
|
||||
templateTitleSection,
|
||||
const SizedBox(height: 24),
|
||||
timeSection,
|
||||
const SizedBox(height: 24),
|
||||
colorSection,
|
||||
const SizedBox(height: 24),
|
||||
pauseSection,
|
||||
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(
|
||||
children: [
|
||||
saveButton,
|
||||
if (widget.template != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
deleteButton,
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return options.baseScreenBuilder(context, widget.onExit, body);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
||||
/// Widget for selecting a color for a template
|
||||
/// All available colors for the templates are displayed in a wrap layout
|
||||
class TemplateColorSelection extends StatelessWidget {
|
||||
///
|
||||
const TemplateColorSelection({
|
||||
required this.selectedColor,
|
||||
required this.onColorSelected,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// The selected color for the template
|
||||
/// If null, no color is selected
|
||||
final int? selectedColor;
|
||||
|
||||
/// Callback for when a color is selected or deselected
|
||||
final void Function(int?) onColorSelected;
|
||||
|
||||
@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 colors = options.colors;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
translations.templateColorLabel,
|
||||
style: textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (var color in colors.templateColors)
|
||||
GestureDetector(
|
||||
onTap: () => _onColorClick(color),
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(
|
||||
color: color.value == selectedColor
|
||||
? Colors.black
|
||||
: Colors.transparent,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: selectedColor == color.value
|
||||
? const Icon(Icons.check)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// If the color is selected, deselect it, otherwise select it
|
||||
void _onColorClick(Color color) => onColorSelected(
|
||||
color.value == selectedColor ? null : color.value,
|
||||
);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
||||
/// An input field for time selection
|
||||
class TimeInputField extends StatelessWidget {
|
||||
///
|
||||
const TimeInputField({
|
||||
required this.initialValue,
|
||||
required this.onTimeChanged,
|
||||
super.key,
|
||||
});
|
||||
|
||||
///
|
||||
final DateTime? initialValue;
|
||||
|
||||
///
|
||||
final void Function(DateTime) onTimeChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
var availabilityScope = AvailabilityScope.of(context);
|
||||
var options = availabilityScope.options;
|
||||
var translations = options.translations;
|
||||
|
||||
Future<void> onFieldtap() async {
|
||||
var time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.fromDateTime(initialValue ?? DateTime.now()),
|
||||
);
|
||||
if (time != null) {
|
||||
onTimeChanged(
|
||||
DateTime(
|
||||
initialValue?.year ?? DateTime.now().year,
|
||||
initialValue?.month ?? DateTime.now().month,
|
||||
initialValue?.day ?? DateTime.now().day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return TextFormField(
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: const Icon(Icons.access_time),
|
||||
hintText: translations.time,
|
||||
hintStyle: theme.inputDecorationTheme.hintStyle,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
initialValue: initialValue != null
|
||||
? translations.timeFormatter(context, initialValue!)
|
||||
: null,
|
||||
readOnly: true,
|
||||
style: options.textStyles.inputFieldTextStyle,
|
||||
onTap: onFieldtap,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
||||
/// Input section for the template name
|
||||
class TemplateNameInput extends StatelessWidget {
|
||||
///
|
||||
const TemplateNameInput({
|
||||
required this.initialValue,
|
||||
required this.onNameChanged,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// The initial value for the template name
|
||||
final String? initialValue;
|
||||
|
||||
/// callback for when the template name is changed
|
||||
final void Function(String) onNameChanged;
|
||||
|
||||
@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;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
translations.templateTitleLabel,
|
||||
style: textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: translations.templateTitleHintText,
|
||||
hintStyle: theme.inputDecorationTheme.hintStyle,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
initialValue: initialValue,
|
||||
style: options.textStyles.inputFieldTextStyle,
|
||||
onChanged: onNameChanged,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/input_fields.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
||||
///
|
||||
class TemplateTimeSelection extends StatelessWidget {
|
||||
///
|
||||
const TemplateTimeSelection({
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.onStartChanged,
|
||||
required this.onEndChanged,
|
||||
super.key,
|
||||
});
|
||||
|
||||
///
|
||||
final DateTime? startTime;
|
||||
|
||||
///
|
||||
final DateTime? endTime;
|
||||
|
||||
///
|
||||
final void Function(DateTime) onStartChanged;
|
||||
|
||||
///
|
||||
final void Function(DateTime) onEndChanged;
|
||||
|
||||
@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;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(translations.time, style: textTheme.titleMedium),
|
||||
const SizedBox(height: 4),
|
||||
Text(translations.templateTimeLabel, style: textTheme.bodyMedium),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: TimeInputField(
|
||||
initialValue: startTime,
|
||||
onTimeChanged: onStartChanged,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
translations.timeSeparator,
|
||||
style: textTheme.bodyMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: TimeInputField(
|
||||
initialValue: endTime,
|
||||
onTimeChanged: onEndChanged,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -317,6 +317,18 @@ class DayTemplateData implements TemplateData {
|
|||
];
|
||||
}
|
||||
|
||||
/// copy the current instance with new values
|
||||
DayTemplateData copyWith({
|
||||
DateTime? startTime,
|
||||
DateTime? endTime,
|
||||
List<AvailabilityBreakModel>? breaks,
|
||||
}) =>
|
||||
DayTemplateData(
|
||||
startTime: startTime ?? this.startTime,
|
||||
endTime: endTime ?? this.endTime,
|
||||
breaks: breaks ?? this.breaks,
|
||||
);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() => {
|
||||
"startTime": startTime.toIso8601String(),
|
||||
|
|
Loading…
Reference in a new issue