mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-19 13:13:44 +02:00
feat: add Semantics widget to inputs and dynamic texts
This will add accessibility id and id to the inputfields, buttons and dynamic texts so they can be accessed in appium for automated tests
This commit is contained in:
parent
2195c7bd76
commit
0721c64859
22 changed files with 755 additions and 294 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
## 1.1.0
|
||||||
|
|
||||||
|
* Added CustomSemantics widget that is used to wrap all the buttons, textfields and dynamic texts to make the userstory accessible for e2e testing.
|
||||||
|
|
||||||
## 1.0.0
|
## 1.0.0
|
||||||
|
|
||||||
- T.B.D
|
* First release of flutter_availability userstory
|
|
@ -0,0 +1,202 @@
|
||||||
|
/// Accessibility identifiers for all widgets in the availability userstory that
|
||||||
|
/// need to be interacted with by e2e tests. This includes buttons, textfields,
|
||||||
|
/// and dynamic texts.
|
||||||
|
class AvailabilityAccessibilityIds {
|
||||||
|
/// default [AvailabilityAccessibilityIds] constructor where all the
|
||||||
|
/// identifiers are required. This is to ensure that apps automatically break
|
||||||
|
/// when new identifiers are added.
|
||||||
|
const AvailabilityAccessibilityIds({
|
||||||
|
required this.monthNameTextIdentifier,
|
||||||
|
required this.previousMonthButtonIdentifier,
|
||||||
|
required this.nextMonthButtonIdentifier,
|
||||||
|
required this.availabilityDateButtonIdentifier,
|
||||||
|
required this.createNewTemplateButtonIdentifier,
|
||||||
|
required this.viewAvailabilitiesButtonIdentifier,
|
||||||
|
required this.clearAvailabilitiesButtonIdentifier,
|
||||||
|
required this.toggleTemplateDrawerButtonIdentifier,
|
||||||
|
required this.availabilitiesPeriodTextIdentifier,
|
||||||
|
required this.selectUnavailableForPeriodButtonIdentifier,
|
||||||
|
required this.addTemplateToAvailabilitiesButtonIdentifier,
|
||||||
|
required this.removeTemplatesFromAvailabilitiesButtonIdentifier,
|
||||||
|
required this.createNewDayTemplateButtonIdentifier,
|
||||||
|
required this.createNewWeekTemplateButtonIdentifier,
|
||||||
|
required this.dayTemplateEditButtonIdentifier,
|
||||||
|
required this.weekTemplateEditButtonIdentifier,
|
||||||
|
required this.templateNameTextFieldIdentifier,
|
||||||
|
required this.startTimeTextFieldIdentifier,
|
||||||
|
required this.endTimeTextFieldIdentifier,
|
||||||
|
required this.durationTextFieldIdentifier,
|
||||||
|
required this.addBreaksButtonIdentifier,
|
||||||
|
required this.editBreakButtonIdentifier,
|
||||||
|
required this.deleteBreakButtonIdentifier,
|
||||||
|
required this.colorSelectionButtonIdentifier,
|
||||||
|
required this.colorSelectedButtonIdentifier,
|
||||||
|
required this.weekDayButtonIdentifier,
|
||||||
|
required this.weekDayTimeIdentifier,
|
||||||
|
required this.weekDayBreakIdentifier,
|
||||||
|
required this.templateNameIdentifier,
|
||||||
|
required this.deleteTemplateButtonIdentifier,
|
||||||
|
required this.saveButtonIdentifier,
|
||||||
|
required this.addButtonIdentifier,
|
||||||
|
required this.nextButtonIdentifier,
|
||||||
|
required this.closeButtonIdentifier,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Empty [AvailabilityAccessibilityIds] constructor where all the identifiers
|
||||||
|
/// are already set to their default values. You can override all or some of
|
||||||
|
/// the default values.
|
||||||
|
const AvailabilityAccessibilityIds.empty({
|
||||||
|
this.monthNameTextIdentifier = "text_month_name",
|
||||||
|
this.previousMonthButtonIdentifier = "button_previous_month",
|
||||||
|
this.nextMonthButtonIdentifier = "button_next_month",
|
||||||
|
this.availabilityDateButtonIdentifier = "button_availability_date",
|
||||||
|
this.createNewTemplateButtonIdentifier = "button_create_template",
|
||||||
|
this.viewAvailabilitiesButtonIdentifier = "button_view_availabilities",
|
||||||
|
this.clearAvailabilitiesButtonIdentifier = "button_clear_availabilities",
|
||||||
|
this.toggleTemplateDrawerButtonIdentifier = "button_toggle_template_drawer",
|
||||||
|
this.availabilitiesPeriodTextIdentifier = "text_availabilities_period",
|
||||||
|
this.selectUnavailableForPeriodButtonIdentifier =
|
||||||
|
"button_select_unavailable_for_period",
|
||||||
|
this.addTemplateToAvailabilitiesButtonIdentifier =
|
||||||
|
"button_add_template_to_availabilities",
|
||||||
|
this.removeTemplatesFromAvailabilitiesButtonIdentifier =
|
||||||
|
"button_remove_templates_from_availabilities",
|
||||||
|
this.createNewDayTemplateButtonIdentifier = "button_create_template_day",
|
||||||
|
this.createNewWeekTemplateButtonIdentifier = "button_create_template_week",
|
||||||
|
this.dayTemplateEditButtonIdentifier = "button_edit_template_day",
|
||||||
|
this.weekTemplateEditButtonIdentifier = "button_edit_template_week",
|
||||||
|
this.templateNameTextFieldIdentifier = "textfield_template_name",
|
||||||
|
this.startTimeTextFieldIdentifier = "textfield_start_time",
|
||||||
|
this.endTimeTextFieldIdentifier = "textfield_end_time",
|
||||||
|
this.durationTextFieldIdentifier = "textfield_duration",
|
||||||
|
this.addBreaksButtonIdentifier = "button_add_breaks",
|
||||||
|
this.editBreakButtonIdentifier = "button_edit_break",
|
||||||
|
this.deleteBreakButtonIdentifier = "button_delete_break",
|
||||||
|
this.colorSelectionButtonIdentifier = "button_select_color",
|
||||||
|
this.colorSelectedButtonIdentifier = "button_selected_color",
|
||||||
|
this.weekDayButtonIdentifier = "button_select_week_day",
|
||||||
|
this.weekDayTimeIdentifier = "text_week_day_time",
|
||||||
|
this.weekDayBreakIdentifier = "text_week_day_break",
|
||||||
|
this.templateNameIdentifier = "text_template_name",
|
||||||
|
this.deleteTemplateButtonIdentifier = "button_delete_template",
|
||||||
|
this.saveButtonIdentifier = "button_save",
|
||||||
|
this.addButtonIdentifier = "button_add",
|
||||||
|
this.nextButtonIdentifier = "button_next",
|
||||||
|
this.closeButtonIdentifier = "button_close",
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The identifier for the text that displays the month that is being viewed
|
||||||
|
final String monthNameTextIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to navigate to the previous month
|
||||||
|
final String previousMonthButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to navigate to the next month
|
||||||
|
final String nextMonthButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to select a date in the availability view
|
||||||
|
/// The month and day are appended to this identifier
|
||||||
|
final String availabilityDateButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to go template overview screen
|
||||||
|
final String createNewTemplateButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to view availabilities
|
||||||
|
final String viewAvailabilitiesButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to clear availabilities;
|
||||||
|
final String clearAvailabilitiesButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to toggle the template drawer
|
||||||
|
final String toggleTemplateDrawerButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the text that displays the period of availabilities
|
||||||
|
/// that are being viewed
|
||||||
|
final String availabilitiesPeriodTextIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the checkbox to clear all availabilities for a period
|
||||||
|
final String selectUnavailableForPeriodButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to add a template to a selection of
|
||||||
|
/// availabilities
|
||||||
|
final String addTemplateToAvailabilitiesButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to remove all templates from a selection of
|
||||||
|
/// availabilities
|
||||||
|
final String removeTemplatesFromAvailabilitiesButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to create a new day template
|
||||||
|
final String createNewDayTemplateButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to create a new week template
|
||||||
|
final String createNewWeekTemplateButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to edit a specific day template, the index
|
||||||
|
/// of the item in the list is appended to this identifier
|
||||||
|
final String dayTemplateEditButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to edit a specific week template, the index
|
||||||
|
/// of the item in the list is appended to this identifier
|
||||||
|
final String weekTemplateEditButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the textfield to edit the name of a template
|
||||||
|
final String templateNameTextFieldIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the textfield to edit a start time
|
||||||
|
final String startTimeTextFieldIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the textfield to edit an end time
|
||||||
|
final String endTimeTextFieldIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the textfield to edit a duration
|
||||||
|
final String durationTextFieldIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to add new breaks
|
||||||
|
final String addBreaksButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the break edit button to edit a specific break, the
|
||||||
|
/// index of the item in the list is appended to this identifier
|
||||||
|
final String editBreakButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the break delete button to delete a specific break, the
|
||||||
|
/// index of the item in the list is appended to this identifier
|
||||||
|
final String deleteBreakButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to select a color from the list of colors,
|
||||||
|
/// the index of the item in the list is appended to this identifier
|
||||||
|
final String colorSelectionButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button for the currently selected color from the
|
||||||
|
/// list of colors. This overrides [colorSelectionButtonIdentifier]
|
||||||
|
final String colorSelectedButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to select a day of the week in the template
|
||||||
|
/// modification screen. The index of the day is appended to this identifier
|
||||||
|
final String weekDayButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the time of a day in the template view
|
||||||
|
/// The index of the day is appended to this identifier
|
||||||
|
final String weekDayTimeIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the time of a break in the template view
|
||||||
|
/// The index of the day and time is appended to this identifier
|
||||||
|
final String weekDayBreakIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the name of a template
|
||||||
|
final String templateNameIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to delete a template
|
||||||
|
final String deleteTemplateButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to save (templates or availabilities)
|
||||||
|
final String saveButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to save breaks
|
||||||
|
final String addButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to navigate to next step for week templates
|
||||||
|
final String nextButtonIdentifier;
|
||||||
|
|
||||||
|
/// The identifier for the button to close a dialog
|
||||||
|
final String closeButtonIdentifier;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_availability/src/config/availability_accessibility_ids.dart";
|
||||||
import "package:flutter_availability/src/config/availability_translations.dart";
|
import "package:flutter_availability/src/config/availability_translations.dart";
|
||||||
import "package:flutter_availability/src/service/errors.dart";
|
import "package:flutter_availability/src/service/errors.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/defaults/default_base_screen.dart";
|
import "package:flutter_availability/src/ui/widgets/defaults/default_base_screen.dart";
|
||||||
|
@ -15,6 +16,7 @@ class AvailabilityOptions {
|
||||||
/// AvailabilityOptions constructor where everything is optional.
|
/// AvailabilityOptions constructor where everything is optional.
|
||||||
AvailabilityOptions({
|
AvailabilityOptions({
|
||||||
this.translations = const AvailabilityTranslations.empty(),
|
this.translations = const AvailabilityTranslations.empty(),
|
||||||
|
this.accessibilityIds = const AvailabilityAccessibilityIds.empty(),
|
||||||
this.baseScreenBuilder = DefaultBaseScreen.builder,
|
this.baseScreenBuilder = DefaultBaseScreen.builder,
|
||||||
this.primaryButtonBuilder = DefaultPrimaryButton.builder,
|
this.primaryButtonBuilder = DefaultPrimaryButton.builder,
|
||||||
this.secondaryButtonBuilder = DefaultSecondaryButton.builder,
|
this.secondaryButtonBuilder = DefaultSecondaryButton.builder,
|
||||||
|
@ -39,6 +41,10 @@ class AvailabilityOptions {
|
||||||
/// The translations for the availability userstory
|
/// The translations for the availability userstory
|
||||||
final AvailabilityTranslations translations;
|
final AvailabilityTranslations translations;
|
||||||
|
|
||||||
|
/// All the accessibility ids for the availability userstory
|
||||||
|
/// These are used to add identifiers to the elements for testing
|
||||||
|
final AvailabilityAccessibilityIds accessibilityIds;
|
||||||
|
|
||||||
/// The implementation for communicating with the persistance layer
|
/// The implementation for communicating with the persistance layer
|
||||||
final AvailabilityDataInterface dataInterface;
|
final AvailabilityDataInterface dataInterface;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "package:flutter_availability/src/ui/widgets/availability_template_select
|
||||||
import "package:flutter_availability/src/ui/widgets/availabillity_time_selection.dart";
|
import "package:flutter_availability/src/ui/widgets/availabillity_time_selection.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/pause_selection.dart";
|
import "package:flutter_availability/src/ui/widgets/pause_selection.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
import "package:flutter_availability/src/util/utils.dart";
|
import "package:flutter_availability/src/util/utils.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
|
@ -62,6 +63,7 @@ class _AvailabilitiesModificationScreenState
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var spacing = options.spacing;
|
var spacing = options.spacing;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
availabilityScope.popHandler.add(widget.onExit);
|
availabilityScope.popHandler.add(widget.onExit);
|
||||||
|
@ -126,10 +128,13 @@ class _AvailabilitiesModificationScreenState
|
||||||
}
|
}
|
||||||
|
|
||||||
var canSave = _availabilityViewModel.canSave;
|
var canSave = _availabilityViewModel.canSave;
|
||||||
var saveButton = options.primaryButtonBuilder(
|
var saveButton = CustomSemantics(
|
||||||
|
identifier: identifiers.saveButtonIdentifier,
|
||||||
|
child: options.primaryButtonBuilder(
|
||||||
context,
|
context,
|
||||||
canSave ? onClickSave : null,
|
canSave ? onClickSave : null,
|
||||||
Text(translations.saveButton),
|
Text(translations.saveButton),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// ignore: avoid_positional_boolean_parameters
|
// ignore: avoid_positional_boolean_parameters
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/calendar.dart";
|
import "package:flutter_availability/src/ui/widgets/calendar.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/template_legend.dart";
|
import "package:flutter_availability/src/ui/widgets/template_legend.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
|
@ -43,6 +44,7 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
||||||
var service = availabilityScope.service;
|
var service = availabilityScope.service;
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
var availabilityStream = useMemoized(
|
var availabilityStream = useMemoized(
|
||||||
() => service.getOverviewDataForMonth(_selectedDate),
|
() => service.getOverviewDataForMonth(_selectedDate),
|
||||||
|
@ -127,16 +129,22 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var clearSelectedButton = options.bigTextButtonBuilder(
|
var clearSelectedButton = CustomSemantics(
|
||||||
|
identifier: identifiers.clearAvailabilitiesButtonIdentifier,
|
||||||
|
child: options.bigTextButtonBuilder(
|
||||||
context,
|
context,
|
||||||
onClearButtonClicked,
|
onClearButtonClicked,
|
||||||
Text(translations.clearAvailabilityButton),
|
Text(translations.clearAvailabilityButton),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var startEditButton = options.primaryButtonBuilder(
|
var startEditButton = CustomSemantics(
|
||||||
|
identifier: identifiers.viewAvailabilitiesButtonIdentifier,
|
||||||
|
child: options.primaryButtonBuilder(
|
||||||
context,
|
context,
|
||||||
onButtonPress,
|
onButtonPress,
|
||||||
Text(translations.editAvailabilityButton),
|
Text(translations.editAvailabilityButton),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return options.baseScreenBuilder(
|
return options.baseScreenBuilder(
|
||||||
|
|
|
@ -4,6 +4,7 @@ import "package:flutter_availability/src/ui/view_models/day_template_view_model.
|
||||||
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
|
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
|
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/template_name_input.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_time_break.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
@ -51,6 +52,7 @@ class _DayTemplateModificationScreenState
|
||||||
var service = availabilityScope.service;
|
var service = availabilityScope.service;
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
availabilityScope.popHandler.add(widget.onExit);
|
availabilityScope.popHandler.add(widget.onExit);
|
||||||
|
@ -101,10 +103,13 @@ class _DayTemplateModificationScreenState
|
||||||
|
|
||||||
var canSave = _viewModel.canSave;
|
var canSave = _viewModel.canSave;
|
||||||
|
|
||||||
var deleteButton = options.bigTextButtonBuilder(
|
var deleteButton = CustomSemantics(
|
||||||
|
identifier: identifiers.deleteTemplateButtonIdentifier,
|
||||||
|
child: options.bigTextButtonBuilder(
|
||||||
context,
|
context,
|
||||||
onDeletePressed,
|
onDeletePressed,
|
||||||
Text(translations.deleteTemplateButton),
|
Text(translations.deleteTemplateButton),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
void onNameChanged(String name) {
|
void onNameChanged(String name) {
|
||||||
|
@ -153,11 +158,14 @@ class _DayTemplateModificationScreenState
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
buttons: [
|
buttons: [
|
||||||
options.primaryButtonBuilder(
|
CustomSemantics(
|
||||||
|
identifier: identifiers.saveButtonIdentifier,
|
||||||
|
child: options.primaryButtonBuilder(
|
||||||
context,
|
context,
|
||||||
canSave ? onSavePressed : null,
|
canSave ? onSavePressed : null,
|
||||||
Text(translations.saveButton),
|
Text(translations.saveButton),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
if (widget.template != null) ...[
|
if (widget.template != null) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
deleteButton,
|
deleteButton,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
|
@ -34,6 +35,7 @@ class AvailabilityTemplateOverview extends HookWidget {
|
||||||
var service = availabilityScope.service;
|
var service = availabilityScope.service;
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
var dayTemplateStream = useMemoized(() => service.getDayTemplates());
|
var dayTemplateStream = useMemoized(() => service.getDayTemplates());
|
||||||
var weekTemplateStream = useMemoized(() => service.getWeekTemplates());
|
var weekTemplateStream = useMemoized(() => service.getWeekTemplates());
|
||||||
|
@ -61,6 +63,7 @@ class AvailabilityTemplateOverview extends HookWidget {
|
||||||
var dayTemplateSection = _TemplateListSection(
|
var dayTemplateSection = _TemplateListSection(
|
||||||
sectionTitle: translations.dayTemplates,
|
sectionTitle: translations.dayTemplates,
|
||||||
createButtonText: translations.createDayTemplate,
|
createButtonText: translations.createDayTemplate,
|
||||||
|
createButtonIdentifier: identifiers.createNewDayTemplateButtonIdentifier,
|
||||||
onEditTemplate: onEditTemplate,
|
onEditTemplate: onEditTemplate,
|
||||||
onSelectTemplate: onSelectTemplate,
|
onSelectTemplate: onSelectTemplate,
|
||||||
onAddTemplate: () => onAddTemplate(AvailabilityTemplateType.day),
|
onAddTemplate: () => onAddTemplate(AvailabilityTemplateType.day),
|
||||||
|
@ -72,6 +75,7 @@ class AvailabilityTemplateOverview extends HookWidget {
|
||||||
var weekTemplateSection = _TemplateListSection(
|
var weekTemplateSection = _TemplateListSection(
|
||||||
sectionTitle: translations.weekTemplates,
|
sectionTitle: translations.weekTemplates,
|
||||||
createButtonText: translations.createWeekTemplate,
|
createButtonText: translations.createWeekTemplate,
|
||||||
|
createButtonIdentifier: identifiers.createNewWeekTemplateButtonIdentifier,
|
||||||
templates: weekTemplates,
|
templates: weekTemplates,
|
||||||
isLoading:
|
isLoading:
|
||||||
weekTemplatesSnapshot.connectionState == ConnectionState.waiting,
|
weekTemplatesSnapshot.connectionState == ConnectionState.waiting,
|
||||||
|
@ -99,6 +103,7 @@ class _TemplateListSection extends StatelessWidget {
|
||||||
const _TemplateListSection({
|
const _TemplateListSection({
|
||||||
required this.sectionTitle,
|
required this.sectionTitle,
|
||||||
required this.createButtonText,
|
required this.createButtonText,
|
||||||
|
required this.createButtonIdentifier,
|
||||||
required this.templates,
|
required this.templates,
|
||||||
required this.isLoading,
|
required this.isLoading,
|
||||||
required this.onEditTemplate,
|
required this.onEditTemplate,
|
||||||
|
@ -108,6 +113,10 @@ class _TemplateListSection extends StatelessWidget {
|
||||||
|
|
||||||
final String sectionTitle;
|
final String sectionTitle;
|
||||||
final String createButtonText;
|
final String createButtonText;
|
||||||
|
|
||||||
|
/// The accessibility identifier for the create button
|
||||||
|
final String createButtonIdentifier;
|
||||||
|
|
||||||
// transform the stream to a snapshot as low as possible to reduce rebuilds
|
// transform the stream to a snapshot as low as possible to reduce rebuilds
|
||||||
final List<AvailabilityTemplateModel> templates;
|
final List<AvailabilityTemplateModel> templates;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
|
@ -140,13 +149,16 @@ class _TemplateListSection extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.add),
|
const Icon(Icons.add),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
options.smallTextButtonBuilder(
|
CustomSemantics(
|
||||||
|
identifier: createButtonIdentifier,
|
||||||
|
child: options.smallTextButtonBuilder(
|
||||||
context,
|
context,
|
||||||
onAddTemplate,
|
onAddTemplate,
|
||||||
Text(
|
Text(
|
||||||
createButtonText,
|
createButtonText,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -157,9 +169,10 @@ class _TemplateListSection extends StatelessWidget {
|
||||||
Text(sectionTitle, style: textTheme.titleMedium),
|
Text(sectionTitle, style: textTheme.titleMedium),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
for (var template in templates) ...[
|
for (var (index, template) in templates.indexed) ...[
|
||||||
_TemplateListSectionItem(
|
_TemplateListSectionItem(
|
||||||
template: template,
|
template: template,
|
||||||
|
index: index,
|
||||||
onTemplateClicked: onClickTemplate,
|
onTemplateClicked: onClickTemplate,
|
||||||
onEditTemplate: onEditTemplate,
|
onEditTemplate: onEditTemplate,
|
||||||
),
|
),
|
||||||
|
@ -177,12 +190,16 @@ class _TemplateListSection extends StatelessWidget {
|
||||||
class _TemplateListSectionItem extends StatelessWidget {
|
class _TemplateListSectionItem extends StatelessWidget {
|
||||||
const _TemplateListSectionItem({
|
const _TemplateListSectionItem({
|
||||||
required this.template,
|
required this.template,
|
||||||
|
required this.index,
|
||||||
required this.onTemplateClicked,
|
required this.onTemplateClicked,
|
||||||
required this.onEditTemplate,
|
required this.onEditTemplate,
|
||||||
});
|
});
|
||||||
|
|
||||||
final AvailabilityTemplateModel template;
|
final AvailabilityTemplateModel template;
|
||||||
|
|
||||||
|
/// The index of the template in the list
|
||||||
|
final int index;
|
||||||
|
|
||||||
final void Function(AvailabilityTemplateModel template) onTemplateClicked;
|
final void Function(AvailabilityTemplateModel template) onTemplateClicked;
|
||||||
final void Function(AvailabilityTemplateModel template) onEditTemplate;
|
final void Function(AvailabilityTemplateModel template) onEditTemplate;
|
||||||
|
|
||||||
|
@ -191,8 +208,16 @@ class _TemplateListSectionItem extends StatelessWidget {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
var templateTypeIdentifer =
|
||||||
|
template.templateType == AvailabilityTemplateType.day
|
||||||
|
? identifiers.dayTemplateEditButtonIdentifier
|
||||||
|
: identifiers.weekTemplateEditButtonIdentifier;
|
||||||
|
var templateIdentifier = "${templateTypeIdentifer}_$index";
|
||||||
|
|
||||||
return InkWell(
|
return CustomSemantics(
|
||||||
|
identifier: templateIdentifier,
|
||||||
|
child: InkWell(
|
||||||
onTap: () => onTemplateClicked(template),
|
onTap: () => onTemplateClicked(template),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
|
@ -230,6 +255,7 @@ class _TemplateListSectionItem extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import "package:flutter_availability/src/service/errors.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/template_daydata_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/view_models/week_template_view_models.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
|
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/template_name_input.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_time_break.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/template_week_day_selection.dart";
|
import "package:flutter_availability/src/ui/widgets/template_week_day_selection.dart";
|
||||||
|
@ -53,6 +54,7 @@ class _WeekTemplateModificationScreenState
|
||||||
var service = availabilityScope.service;
|
var service = availabilityScope.service;
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
var spacing = options.spacing;
|
var spacing = options.spacing;
|
||||||
|
|
||||||
var weekTemplateDate = _viewModel.data;
|
var weekTemplateDate = _viewModel.data;
|
||||||
|
@ -134,22 +136,31 @@ class _WeekTemplateModificationScreenState
|
||||||
});
|
});
|
||||||
|
|
||||||
var canSave = _viewModel.canSave;
|
var canSave = _viewModel.canSave;
|
||||||
var nextButton = options.primaryButtonBuilder(
|
var nextButton = CustomSemantics(
|
||||||
|
identifier: identifiers.nextButtonIdentifier,
|
||||||
|
child: options.primaryButtonBuilder(
|
||||||
context,
|
context,
|
||||||
canSave ? onNextPressed : null,
|
canSave ? onNextPressed : null,
|
||||||
Text(translations.nextButton),
|
Text(translations.nextButton),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var saveButton = options.primaryButtonBuilder(
|
var saveButton = CustomSemantics(
|
||||||
|
identifier: identifiers.saveButtonIdentifier,
|
||||||
|
child: options.primaryButtonBuilder(
|
||||||
context,
|
context,
|
||||||
canSave ? onSavePressed : null,
|
canSave ? onSavePressed : null,
|
||||||
Text(translations.saveButton),
|
Text(translations.saveButton),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var deleteButton = options.bigTextButtonBuilder(
|
var deleteButton = CustomSemantics(
|
||||||
|
identifier: identifiers.deleteTemplateButtonIdentifier,
|
||||||
|
child: options.bigTextButtonBuilder(
|
||||||
context,
|
context,
|
||||||
onDeletePressed,
|
onDeletePressed,
|
||||||
Text(translations.deleteTemplateButton),
|
Text(translations.deleteTemplateButton),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var title = Center(
|
var title = Center(
|
||||||
|
@ -230,12 +241,15 @@ class _WeekTemplateModificationScreenState
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
child: CustomSemantics(
|
||||||
|
identifier: identifiers.templateNameIdentifier,
|
||||||
child: Text(
|
child: Text(
|
||||||
_viewModel.name ?? "",
|
_viewModel.name ?? "",
|
||||||
style: textTheme.bodyLarge,
|
style: textTheme.bodyLarge,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -30,6 +31,7 @@ class AvailabilityClearSection extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
var isSingleDay = range.start.isAtSameMomentAs(range.end);
|
var isSingleDay = range.start.isAtSameMomentAs(range.end);
|
||||||
|
|
||||||
|
@ -43,14 +45,20 @@ class AvailabilityClearSection extends StatelessWidget {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
CustomSemantics(
|
||||||
|
identifier: identifiers.availabilitiesPeriodTextIdentifier,
|
||||||
|
child: Text(
|
||||||
titleText,
|
titleText,
|
||||||
style: textTheme.titleMedium,
|
style: textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Checkbox(
|
CustomSemantics(
|
||||||
|
identifier:
|
||||||
|
identifiers.selectUnavailableForPeriodButtonIdentifier,
|
||||||
|
child: Checkbox(
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
splashRadius: 0,
|
splashRadius: 0,
|
||||||
|
@ -60,6 +68,7 @@ class AvailabilityClearSection extends StatelessWidget {
|
||||||
onChanged(value);
|
onChanged(value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
unavailableText,
|
unavailableText,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ class AvailabilityTemplateSelection extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
var titleText = translations.availabilityAddTemplateTitle;
|
var titleText = translations.availabilityAddTemplateTitle;
|
||||||
if (selectedTemplates.isNotEmpty) {
|
if (selectedTemplates.isNotEmpty) {
|
||||||
|
@ -47,11 +49,14 @@ class AvailabilityTemplateSelection extends StatelessWidget {
|
||||||
var addButton = options.bigTextButtonWrapperBuilder(
|
var addButton = options.bigTextButtonWrapperBuilder(
|
||||||
context,
|
context,
|
||||||
onTemplateAdd,
|
onTemplateAdd,
|
||||||
options.bigTextButtonBuilder(
|
CustomSemantics(
|
||||||
|
identifier: identifiers.addTemplateToAvailabilitiesButtonIdentifier,
|
||||||
|
child: options.bigTextButtonBuilder(
|
||||||
context,
|
context,
|
||||||
onTemplateAdd,
|
onTemplateAdd,
|
||||||
Text(translations.addButton),
|
Text(translations.addButton),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -89,6 +94,7 @@ class _TemplateList extends StatelessWidget {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
|
@ -107,8 +113,8 @@ class _TemplateList extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
for (var template in selectedTemplates) ...[
|
for (var (index, template) in selectedTemplates.indexed) ...[
|
||||||
_TemplateListItem(template: template),
|
_TemplateListItem(template: template, index: index),
|
||||||
if (template != selectedTemplates.last) ...[
|
if (template != selectedTemplates.last) ...[
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
|
@ -117,10 +123,14 @@ class _TemplateList extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
InkWell(
|
CustomSemantics(
|
||||||
|
identifier:
|
||||||
|
identifiers.removeTemplatesFromAvailabilitiesButtonIdentifier,
|
||||||
|
child: InkWell(
|
||||||
onTap: onTemplatesRemoved,
|
onTap: onTemplatesRemoved,
|
||||||
child: const Icon(Icons.remove),
|
child: const Icon(Icons.remove),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -128,15 +138,22 @@ class _TemplateList extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TemplateListItem extends StatelessWidget {
|
class _TemplateListItem extends StatelessWidget {
|
||||||
const _TemplateListItem({required this.template});
|
const _TemplateListItem({
|
||||||
|
required this.template,
|
||||||
|
required this.index,
|
||||||
|
});
|
||||||
|
|
||||||
final AvailabilityTemplateModel template;
|
final AvailabilityTemplateModel template;
|
||||||
|
|
||||||
|
/// The index of the template in the list of selected templates
|
||||||
|
final int index;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -150,12 +167,15 @@ class _TemplateListItem extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
child: CustomSemantics(
|
||||||
|
identifier: "${identifiers.templateNameIdentifier}_$index",
|
||||||
child: Text(
|
child: Text(
|
||||||
template.name,
|
template.name,
|
||||||
style: theme.textTheme.bodyLarge,
|
style: theme.textTheme.bodyLarge,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_availability/flutter_availability.dart";
|
import "package:flutter_availability/flutter_availability.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/calendar_grid.dart";
|
import "package:flutter_availability/src/ui/widgets/calendar_grid.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -65,6 +66,7 @@ class CalendarView extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
var mappedCalendarDays = _mapAvailabilitiesToCalendarDays(availabilities);
|
var mappedCalendarDays = _mapAvailabilitiesToCalendarDays(availabilities);
|
||||||
var existsTemplateDeviations = mappedCalendarDays.any(
|
var existsTemplateDeviations = mappedCalendarDays.any(
|
||||||
|
@ -74,7 +76,9 @@ class CalendarView extends StatelessWidget {
|
||||||
var monthDateSelector = Row(
|
var monthDateSelector = Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
CustomSemantics(
|
||||||
|
identifier: identifiers.previousMonthButtonIdentifier,
|
||||||
|
child: IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
icon: const Icon(Icons.chevron_left),
|
icon: const Icon(Icons.chevron_left),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -83,17 +87,23 @@ class CalendarView extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 44),
|
const SizedBox(width: 44),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: _calculateTextWidthOfLongestMonth(context, translations),
|
width: _calculateTextWidthOfLongestMonth(context, translations),
|
||||||
|
child: CustomSemantics(
|
||||||
|
identifier: identifiers.monthNameTextIdentifier,
|
||||||
child: Text(
|
child: Text(
|
||||||
translations.monthYearFormatter(context, month),
|
translations.monthYearFormatter(context, month),
|
||||||
style: textTheme.titleMedium,
|
style: textTheme.titleMedium,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 44),
|
const SizedBox(width: 44),
|
||||||
IconButton(
|
CustomSemantics(
|
||||||
|
identifier: identifiers.nextMonthButtonIdentifier,
|
||||||
|
child: IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
icon: const Icon(Icons.chevron_right),
|
icon: const Icon(Icons.chevron_right),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -102,6 +112,7 @@ class CalendarView extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_availability/flutter_availability.dart";
|
import "package:flutter_availability/flutter_availability.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
/// Returns the days of the week as abbreviated strings
|
/// Returns the days of the week as abbreviated strings
|
||||||
|
@ -119,6 +120,7 @@ class _CalendarDay extends StatelessWidget {
|
||||||
var colorScheme = theme.colorScheme;
|
var colorScheme = theme.colorScheme;
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
var colors = options.colors;
|
var colors = options.colors;
|
||||||
|
|
||||||
var dayColor = day.color ??
|
var dayColor = day.color ??
|
||||||
|
@ -134,6 +136,10 @@ class _CalendarDay extends StatelessWidget {
|
||||||
textStyle = textTheme.titleMedium?.copyWith(color: textColor);
|
textStyle = textTheme.titleMedium?.copyWith(color: textColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dayIdentifier =
|
||||||
|
"${identifiers.availabilityDateButtonIdentifier}_${day.date.year}_"
|
||||||
|
"${day.date.month}_${day.date.day}";
|
||||||
|
|
||||||
var decoration = day.outsideMonth
|
var decoration = day.outsideMonth
|
||||||
? null
|
? null
|
||||||
: BoxDecoration(
|
: BoxDecoration(
|
||||||
|
@ -145,7 +151,9 @@ class _CalendarDay extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return InkWell(
|
return CustomSemantics(
|
||||||
|
identifier: dayIdentifier,
|
||||||
|
child: InkWell(
|
||||||
onTap: () => onDayTap(day.date),
|
onTap: () => onDayTap(day.date),
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
@ -173,6 +181,7 @@ class _CalendarDay extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import "dart:math";
|
import "dart:math";
|
||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
/// Widget for selecting a color for a template
|
/// Widget for selecting a color for a template
|
||||||
|
@ -40,9 +41,10 @@ class TemplateColorSelection extends StatelessWidget {
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: [
|
children: [
|
||||||
for (var color in colors.templateColors) ...[
|
for (var (index, color) in colors.templateColors.indexed) ...[
|
||||||
_TemplateColorItem(
|
_TemplateColorItem(
|
||||||
color: color,
|
color: color,
|
||||||
|
index: index,
|
||||||
selectedColor: selectedColor,
|
selectedColor: selectedColor,
|
||||||
onColorSelected: onColorSelected,
|
onColorSelected: onColorSelected,
|
||||||
),
|
),
|
||||||
|
@ -57,6 +59,7 @@ class TemplateColorSelection extends StatelessWidget {
|
||||||
class _TemplateColorItem extends StatelessWidget {
|
class _TemplateColorItem extends StatelessWidget {
|
||||||
const _TemplateColorItem({
|
const _TemplateColorItem({
|
||||||
required this.color,
|
required this.color,
|
||||||
|
required this.index,
|
||||||
required this.selectedColor,
|
required this.selectedColor,
|
||||||
required this.onColorSelected,
|
required this.onColorSelected,
|
||||||
});
|
});
|
||||||
|
@ -66,14 +69,20 @@ class _TemplateColorItem extends StatelessWidget {
|
||||||
|
|
||||||
final Color color;
|
final Color color;
|
||||||
|
|
||||||
|
/// The index of the color in the list of colors
|
||||||
|
final int index;
|
||||||
|
|
||||||
final int? selectedColor;
|
final int? selectedColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
var colors = options.colors;
|
var colors = options.colors;
|
||||||
|
|
||||||
|
var isSelected = selectedColor == color.value;
|
||||||
|
|
||||||
/// If the color is selected, deselect it, otherwise select it
|
/// If the color is selected, deselect it, otherwise select it
|
||||||
void onColorClick(Color color) => onColorSelected(
|
void onColorClick(Color color) => onColorSelected(
|
||||||
color.value == selectedColor ? null : color.value,
|
color.value == selectedColor ? null : color.value,
|
||||||
|
@ -83,11 +92,15 @@ class _TemplateColorItem extends StatelessWidget {
|
||||||
? colors.templateColorLightCheckmarkColor
|
? colors.templateColorLightCheckmarkColor
|
||||||
: colors.templateColorDarkCheckmarkColor;
|
: colors.templateColorDarkCheckmarkColor;
|
||||||
|
|
||||||
var icon = selectedColor == color.value
|
var icon = isSelected ? Icon(Icons.check, color: checkMarkColor) : null;
|
||||||
? Icon(Icons.check, color: checkMarkColor)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return GestureDetector(
|
var colorIdentifier = isSelected
|
||||||
|
? identifiers.colorSelectedButtonIdentifier
|
||||||
|
: "${identifiers.colorSelectionButtonIdentifier}_$index";
|
||||||
|
|
||||||
|
return CustomSemantics(
|
||||||
|
identifier: colorIdentifier,
|
||||||
|
child: GestureDetector(
|
||||||
onTap: () => onColorClick(color),
|
onTap: () => onColorClick(color),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
|
@ -98,6 +111,7 @@ class _TemplateColorItem extends StatelessWidget {
|
||||||
),
|
),
|
||||||
child: icon,
|
child: icon,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ class TimeSelection extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: crossAxisAlignment,
|
crossAxisAlignment: crossAxisAlignment,
|
||||||
|
@ -63,6 +64,7 @@ class TimeSelection extends StatelessWidget {
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: TimeInputField(
|
child: TimeInputField(
|
||||||
|
identifier: identifiers.startTimeTextFieldIdentifier,
|
||||||
initialValue: startTime,
|
initialValue: startTime,
|
||||||
onTimeChanged: onStartChanged,
|
onTimeChanged: onStartChanged,
|
||||||
),
|
),
|
||||||
|
@ -78,6 +80,7 @@ class TimeSelection extends StatelessWidget {
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: TimeInputField(
|
child: TimeInputField(
|
||||||
|
identifier: identifiers.endTimeTextFieldIdentifier,
|
||||||
initialValue: endTime,
|
initialValue: endTime,
|
||||||
onTimeChanged: onEndChanged,
|
onTimeChanged: onEndChanged,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter/services.dart";
|
import "package:flutter/services.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
/// An input field for time selection
|
/// An input field for time selection
|
||||||
|
@ -8,12 +9,16 @@ class TimeInputField extends StatelessWidget {
|
||||||
const TimeInputField({
|
const TimeInputField({
|
||||||
required this.initialValue,
|
required this.initialValue,
|
||||||
required this.onTimeChanged,
|
required this.onTimeChanged,
|
||||||
|
required this.identifier,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
///
|
///
|
||||||
final TimeOfDay? initialValue;
|
final TimeOfDay? initialValue;
|
||||||
|
|
||||||
|
/// The accessibility identifier for this input field
|
||||||
|
final String identifier;
|
||||||
|
|
||||||
///
|
///
|
||||||
final void Function(TimeOfDay) onTimeChanged;
|
final void Function(TimeOfDay) onTimeChanged;
|
||||||
|
|
||||||
|
@ -37,7 +42,10 @@ class TimeInputField extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TextFormField(
|
return CustomSemantics(
|
||||||
|
identifier: identifier,
|
||||||
|
isTextField: true,
|
||||||
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
suffixIcon: const Icon(Icons.access_time),
|
suffixIcon: const Icon(Icons.access_time),
|
||||||
hintText: translations.time,
|
hintText: translations.time,
|
||||||
|
@ -52,6 +60,7 @@ class TimeInputField extends StatelessWidget {
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
style: options.textStyles.inputFieldTextStyle,
|
style: options.textStyles.inputFieldTextStyle,
|
||||||
onTap: onFieldtap,
|
onTap: onFieldtap,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,6 +131,7 @@ class _DurationInputFieldState extends State<DurationInputField> {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
return Focus(
|
return Focus(
|
||||||
onFocusChange: (hasFocus) {
|
onFocusChange: (hasFocus) {
|
||||||
|
@ -131,6 +141,9 @@ class _DurationInputFieldState extends State<DurationInputField> {
|
||||||
_removeOverlay();
|
_removeOverlay();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
child: CustomSemantics(
|
||||||
|
identifier: identifiers.durationTextFieldIdentifier,
|
||||||
|
isTextField: true,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: translations.time,
|
labelText: translations.time,
|
||||||
|
@ -148,6 +161,7 @@ class _DurationInputFieldState extends State<DurationInputField> {
|
||||||
FilteringTextInputFormatter.digitsOnly,
|
FilteringTextInputFormatter.digitsOnly,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import "package:flutter_availability/src/service/pop_handler.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/break_view_model.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/generic_time_selection.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/input_fields.dart";
|
import "package:flutter_availability/src/ui/widgets/input_fields.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -32,6 +33,7 @@ class PauseSelection extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
var popHandler = availabilityScope.popHandler;
|
var popHandler = availabilityScope.popHandler;
|
||||||
|
|
||||||
Future<BreakViewModel?> openBreakDialog(
|
Future<BreakViewModel?> openBreakDialog(
|
||||||
|
@ -73,7 +75,9 @@ class PauseSelection extends StatelessWidget {
|
||||||
|
|
||||||
var sortedBreaks = breaks.toList()..sort((a, b) => a.compareTo(b));
|
var sortedBreaks = breaks.toList()..sort((a, b) => a.compareTo(b));
|
||||||
|
|
||||||
var addButton = options.bigTextButtonWrapperBuilder(
|
var addButton = CustomSemantics(
|
||||||
|
identifier: identifiers.addBreaksButtonIdentifier,
|
||||||
|
child: options.bigTextButtonWrapperBuilder(
|
||||||
context,
|
context,
|
||||||
onClickAddBreak,
|
onClickAddBreak,
|
||||||
options.bigTextButtonBuilder(
|
options.bigTextButtonBuilder(
|
||||||
|
@ -81,6 +85,7 @@ class PauseSelection extends StatelessWidget {
|
||||||
onClickAddBreak,
|
onClickAddBreak,
|
||||||
Text(translations.addButton),
|
Text(translations.addButton),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -99,10 +104,11 @@ class PauseSelection extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
for (var breakModel in sortedBreaks) ...[
|
for (var (index, breakModel) in sortedBreaks.indexed) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
BreakDisplay(
|
BreakDisplay(
|
||||||
breakModel: breakModel,
|
breakModel: breakModel,
|
||||||
|
index: index,
|
||||||
onRemove: () => onDeleteBreak(breakModel),
|
onRemove: () => onDeleteBreak(breakModel),
|
||||||
onClick: () async => onEditBreak(breakModel),
|
onClick: () async => onEditBreak(breakModel),
|
||||||
),
|
),
|
||||||
|
@ -119,6 +125,7 @@ class BreakDisplay extends StatelessWidget {
|
||||||
/// Creates a new break display
|
/// Creates a new break display
|
||||||
const BreakDisplay({
|
const BreakDisplay({
|
||||||
required this.breakModel,
|
required this.breakModel,
|
||||||
|
required this.index,
|
||||||
required this.onRemove,
|
required this.onRemove,
|
||||||
required this.onClick,
|
required this.onClick,
|
||||||
super.key,
|
super.key,
|
||||||
|
@ -127,6 +134,9 @@ class BreakDisplay extends StatelessWidget {
|
||||||
/// The break to display
|
/// The break to display
|
||||||
final BreakViewModel breakModel;
|
final BreakViewModel breakModel;
|
||||||
|
|
||||||
|
/// The index of the break in the list
|
||||||
|
final int index;
|
||||||
|
|
||||||
/// Callback for when the minus button is clicked
|
/// Callback for when the minus button is clicked
|
||||||
final VoidCallback onRemove;
|
final VoidCallback onRemove;
|
||||||
|
|
||||||
|
@ -140,6 +150,7 @@ class BreakDisplay extends StatelessWidget {
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var colors = options.colors;
|
var colors = options.colors;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
var starTime = translations.timeFormatter(
|
var starTime = translations.timeFormatter(
|
||||||
context,
|
context,
|
||||||
|
@ -151,6 +162,9 @@ class BreakDisplay extends StatelessWidget {
|
||||||
);
|
);
|
||||||
|
|
||||||
var breakDuration = breakModel.duration.inMinutes;
|
var breakDuration = breakModel.duration.inMinutes;
|
||||||
|
var editBreakIdentifier = "${identifiers.editBreakButtonIdentifier}_$index";
|
||||||
|
var deleteBreakIdentifier =
|
||||||
|
"${identifiers.deleteBreakButtonIdentifier}_$index";
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: onClick,
|
onTap: onClick,
|
||||||
|
@ -163,17 +177,23 @@ class BreakDisplay extends StatelessWidget {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
CustomSemantics(
|
||||||
|
identifier: editBreakIdentifier,
|
||||||
|
child: Text(
|
||||||
"$breakDuration "
|
"$breakDuration "
|
||||||
"${translations.timeMinutes} | "
|
"${translations.timeMinutes} | "
|
||||||
"$starTime - "
|
"$starTime - "
|
||||||
"$endTime",
|
"$endTime",
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
InkWell(
|
CustomSemantics(
|
||||||
|
identifier: deleteBreakIdentifier,
|
||||||
|
child: InkWell(
|
||||||
onTap: onRemove,
|
onTap: onRemove,
|
||||||
child: const Icon(Icons.remove),
|
child: const Icon(Icons.remove),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -251,6 +271,7 @@ class _AvailabilityBreakSelectionDialogState
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
var spacing = options.spacing;
|
var spacing = options.spacing;
|
||||||
|
|
||||||
void onUpdateDuration(Duration? duration) {
|
void onUpdateDuration(Duration? duration) {
|
||||||
|
@ -299,7 +320,9 @@ class _AvailabilityBreakSelectionDialogState
|
||||||
|
|
||||||
var onSaveButtonPress = canSave ? onSave : null;
|
var onSaveButtonPress = canSave ? onSave : null;
|
||||||
|
|
||||||
var saveButton = options.primaryButtonBuilder(
|
var saveButton = CustomSemantics(
|
||||||
|
identifier: identifiers.addButtonIdentifier,
|
||||||
|
child: options.primaryButtonBuilder(
|
||||||
context,
|
context,
|
||||||
onSaveButtonPress,
|
onSaveButtonPress,
|
||||||
Text(
|
Text(
|
||||||
|
@ -307,6 +330,7 @@ class _AvailabilityBreakSelectionDialogState
|
||||||
? translations.addButton
|
? translations.addButton
|
||||||
: translations.saveButton,
|
: translations.saveButton,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var descriptionText = widget.editingTemplate
|
var descriptionText = widget.editingTemplate
|
||||||
|
@ -381,12 +405,15 @@ class _AvailabilityBreakSelectionDialogState
|
||||||
Positioned(
|
Positioned(
|
||||||
right: 0,
|
right: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
|
child: CustomSemantics(
|
||||||
|
identifier: identifiers.closeButtonIdentifier,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_availability/src/config/availability_options.dart";
|
import "package:flutter_availability/src/config/availability_options.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var colors = options.colors;
|
var colors = options.colors;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
var featureSet = options.featureSet;
|
var featureSet = options.featureSet;
|
||||||
|
|
||||||
var templatesLoading =
|
var templatesLoading =
|
||||||
|
@ -63,7 +65,9 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var createNewTemplateButton = GestureDetector(
|
var createNewTemplateButton = CustomSemantics(
|
||||||
|
identifier: identifiers.createNewTemplateButtonIdentifier,
|
||||||
|
child: GestureDetector(
|
||||||
onTap: () => widget.onViewTemplates(),
|
onTap: () => widget.onViewTemplates(),
|
||||||
child: ColoredBox(
|
child: ColoredBox(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
|
@ -79,6 +83,7 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget body = const Divider(
|
Widget body = const Divider(
|
||||||
|
@ -104,6 +109,7 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
left: 12,
|
left: 12,
|
||||||
),
|
),
|
||||||
child: _TemplateLegendItem(
|
child: _TemplateLegendItem(
|
||||||
|
index: 0,
|
||||||
name: translations.templateSelectionLabel,
|
name: translations.templateSelectionLabel,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
borderColor: colorScheme.primary,
|
borderColor: colorScheme.primary,
|
||||||
|
@ -116,6 +122,7 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
left: 12,
|
left: 12,
|
||||||
),
|
),
|
||||||
child: _TemplateLegendItem(
|
child: _TemplateLegendItem(
|
||||||
|
index: 1,
|
||||||
name: translations.availabilityWithoutTemplateLabel,
|
name: translations.availabilityWithoutTemplateLabel,
|
||||||
backgroundColor: colors.customAvailabilityColor ??
|
backgroundColor: colors.customAvailabilityColor ??
|
||||||
colorScheme.secondary,
|
colorScheme.secondary,
|
||||||
|
@ -123,13 +130,14 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
if (featureSet.require(AvailabilityFeature.templates)) ...[
|
if (featureSet.require(AvailabilityFeature.templates)) ...[
|
||||||
for (var template in templates) ...[
|
for (var (index, template) in templates.indexed) ...[
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 10,
|
top: 10,
|
||||||
left: 12,
|
left: 12,
|
||||||
),
|
),
|
||||||
child: _TemplateLegendItem(
|
child: _TemplateLegendItem(
|
||||||
|
index: index + 2,
|
||||||
name: template.name,
|
name: template.name,
|
||||||
backgroundColor: Color(template.color),
|
backgroundColor: Color(template.color),
|
||||||
),
|
),
|
||||||
|
@ -149,7 +157,9 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// a button to open/close a drawer with all the templates
|
// a button to open/close a drawer with all the templates
|
||||||
GestureDetector(
|
CustomSemantics(
|
||||||
|
identifier: identifiers.toggleTemplateDrawerButtonIdentifier,
|
||||||
|
child: GestureDetector(
|
||||||
onTap: onDrawerHeaderClick,
|
onTap: onDrawerHeaderClick,
|
||||||
child: ColoredBox(
|
child: ColoredBox(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
|
@ -173,6 +183,7 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
AnimatedContainer(
|
AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
|
@ -208,11 +219,17 @@ class _TemplateLegendItem extends StatelessWidget {
|
||||||
const _TemplateLegendItem({
|
const _TemplateLegendItem({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.backgroundColor,
|
required this.backgroundColor,
|
||||||
|
required this.index,
|
||||||
this.borderColor,
|
this.borderColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
|
/// The index of the color in the list of colors (index 0 is the selected
|
||||||
|
/// color template, index 1 is the color for availabilities without a
|
||||||
|
/// template)
|
||||||
|
final int index;
|
||||||
|
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
|
|
||||||
final Color? borderColor;
|
final Color? borderColor;
|
||||||
|
@ -222,6 +239,9 @@ class _TemplateLegendItem extends StatelessWidget {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
|
var templateIdentifier = "${identifiers.templateNameIdentifier}_$index";
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -238,12 +258,15 @@ class _TemplateLegendItem extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
child: CustomSemantics(
|
||||||
|
identifier: templateIdentifier,
|
||||||
child: Text(
|
child: Text(
|
||||||
name,
|
name,
|
||||||
style: theme.textTheme.bodyLarge,
|
style: theme.textTheme.bodyLarge,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
/// Input section for the template name
|
/// Input section for the template name
|
||||||
|
@ -23,6 +24,7 @@ class TemplateNameInput extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -32,7 +34,10 @@ class TemplateNameInput extends StatelessWidget {
|
||||||
style: textTheme.titleMedium,
|
style: textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
TextFormField(
|
CustomSemantics(
|
||||||
|
identifier: identifiers.templateNameTextFieldIdentifier,
|
||||||
|
isTextField: true,
|
||||||
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: translations.templateTitleHintText,
|
hintText: translations.templateTitleHintText,
|
||||||
hintStyle: theme.inputDecorationTheme.hintStyle,
|
hintStyle: theme.inputDecorationTheme.hintStyle,
|
||||||
|
@ -46,6 +51,7 @@ class TemplateNameInput extends StatelessWidget {
|
||||||
style: options.textStyles.inputFieldTextStyle,
|
style: options.textStyles.inputFieldTextStyle,
|
||||||
onChanged: onNameChanged,
|
onChanged: onNameChanged,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/calendar_grid.dart";
|
import "package:flutter_availability/src/ui/widgets/calendar_grid.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
/// A widget for selecting a day of the week
|
/// A widget for selecting a day of the week
|
||||||
|
@ -58,10 +59,10 @@ class _TemplateWeekDaySelectionState extends State<TemplateWeekDaySelection> {
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
for (var day in days) ...[
|
for (var (index, day) in days.indexed) ...[
|
||||||
_DaySelectionCard(
|
_DaySelectionCard(
|
||||||
day: day,
|
day: day,
|
||||||
days: days,
|
index: index,
|
||||||
selectedDayIndex: _selectedDayIndex,
|
selectedDayIndex: _selectedDayIndex,
|
||||||
onDaySelected: (selected) =>
|
onDaySelected: (selected) =>
|
||||||
onDaySelected(selected, days.indexOf(day)),
|
onDaySelected(selected, days.indexOf(day)),
|
||||||
|
@ -81,12 +82,12 @@ class _DaySelectionCard extends StatelessWidget {
|
||||||
const _DaySelectionCard({
|
const _DaySelectionCard({
|
||||||
required this.selectedDayIndex,
|
required this.selectedDayIndex,
|
||||||
required this.day,
|
required this.day,
|
||||||
required this.days,
|
required this.index,
|
||||||
required this.onDaySelected,
|
required this.onDaySelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String day;
|
final String day;
|
||||||
final List<String> days;
|
final int index;
|
||||||
|
|
||||||
final int selectedDayIndex;
|
final int selectedDayIndex;
|
||||||
|
|
||||||
|
@ -94,11 +95,11 @@ class _DaySelectionCard extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var index = days.indexOf(day);
|
|
||||||
var isSelected = index == selectedDayIndex;
|
var isSelected = index == selectedDayIndex;
|
||||||
|
|
||||||
return _DaySelectionCardLayout(
|
return _DaySelectionCardLayout(
|
||||||
day: day,
|
day: day,
|
||||||
|
index: index,
|
||||||
isSelected: isSelected,
|
isSelected: isSelected,
|
||||||
onDaySelected: onDaySelected,
|
onDaySelected: onDaySelected,
|
||||||
);
|
);
|
||||||
|
@ -108,6 +109,7 @@ class _DaySelectionCard extends StatelessWidget {
|
||||||
class _DaySelectionCardLayout extends StatelessWidget {
|
class _DaySelectionCardLayout extends StatelessWidget {
|
||||||
const _DaySelectionCardLayout({
|
const _DaySelectionCardLayout({
|
||||||
required this.day,
|
required this.day,
|
||||||
|
required this.index,
|
||||||
required this.isSelected,
|
required this.isSelected,
|
||||||
required this.onDaySelected,
|
required this.onDaySelected,
|
||||||
});
|
});
|
||||||
|
@ -115,6 +117,9 @@ class _DaySelectionCardLayout extends StatelessWidget {
|
||||||
final String day;
|
final String day;
|
||||||
final bool isSelected;
|
final bool isSelected;
|
||||||
|
|
||||||
|
/// The index of the day in the list of days
|
||||||
|
final int index;
|
||||||
|
|
||||||
final void Function(bool) onDaySelected;
|
final void Function(bool) onDaySelected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -124,6 +129,7 @@ class _DaySelectionCardLayout extends StatelessWidget {
|
||||||
var abbreviationTextStyle = textTheme.headlineMedium;
|
var abbreviationTextStyle = textTheme.headlineMedium;
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
abbreviationTextStyle = isSelected
|
abbreviationTextStyle = isSelected
|
||||||
? abbreviationTextStyle?.copyWith(
|
? abbreviationTextStyle?.copyWith(
|
||||||
|
@ -131,10 +137,14 @@ class _DaySelectionCardLayout extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: abbreviationTextStyle;
|
: abbreviationTextStyle;
|
||||||
|
|
||||||
|
var identifier = "${identifiers.weekDayButtonIdentifier}_$index";
|
||||||
|
|
||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
height: isSelected ? 72 : 64,
|
height: isSelected ? 72 : 64,
|
||||||
width: isSelected ? 72 : 64,
|
width: isSelected ? 72 : 64,
|
||||||
|
child: CustomSemantics(
|
||||||
|
identifier: identifier,
|
||||||
child: ChoiceChip(
|
child: ChoiceChip(
|
||||||
shape: RoundedRectangleBorder(borderRadius: options.borderRadius),
|
shape: RoundedRectangleBorder(borderRadius: options.borderRadius),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
@ -148,6 +158,7 @@ class _DaySelectionCardLayout extends StatelessWidget {
|
||||||
showCheckmark: theme.chipTheme.showCheckmark ?? false,
|
showCheckmark: theme.chipTheme.showCheckmark ?? false,
|
||||||
onSelected: onDaySelected,
|
onSelected: onDaySelected,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ 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/template_daydata_view_model.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/week_template_view_models.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/ui/widgets/calendar_grid.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/semantic_widget.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
|
|
||||||
|
@ -28,12 +29,24 @@ class TemplateWeekOverview extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
var colors = options.colors;
|
var colors = options.colors;
|
||||||
|
|
||||||
var dayNames = getDaysOfTheWeekAsStrings(translations, context);
|
var dayNames = getDaysOfTheWeekAsStrings(translations, context);
|
||||||
|
|
||||||
var templateData = template.data;
|
var templateData = template.data;
|
||||||
|
|
||||||
|
var editButton = CustomSemantics(
|
||||||
|
identifier: identifiers.weekTemplateEditButtonIdentifier,
|
||||||
|
child: options.smallTextButtonBuilder(
|
||||||
|
context,
|
||||||
|
onClickEdit,
|
||||||
|
Text(
|
||||||
|
translations.editTemplateButton,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -46,13 +59,7 @@ class TemplateWeekOverview extends StatelessWidget {
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
options.smallTextButtonBuilder(
|
editButton,
|
||||||
context,
|
|
||||||
onClickEdit,
|
|
||||||
Text(
|
|
||||||
translations.editTemplateButton,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
@ -67,12 +74,13 @@ class TemplateWeekOverview extends StatelessWidget {
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
for (var day in WeekDay.values) ...[
|
for (var (index, day) in WeekDay.values.indexed) ...[
|
||||||
_TemplateDayDetailRow(
|
_TemplateDayDetailRow(
|
||||||
dayName: dayNames[day.index],
|
dayName: dayNames[index],
|
||||||
dayData:
|
dayData:
|
||||||
templateData.containsKey(day) ? templateData[day] : null,
|
templateData.containsKey(day) ? templateData[day] : null,
|
||||||
isOdd: day.index.isOdd,
|
index: index,
|
||||||
|
isOdd: index.isOdd,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -88,6 +96,7 @@ class _TemplateDayDetailRow extends StatelessWidget {
|
||||||
required this.dayName,
|
required this.dayName,
|
||||||
required this.dayData,
|
required this.dayData,
|
||||||
required this.isOdd,
|
required this.isOdd,
|
||||||
|
required this.index,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The name of the day
|
/// The name of the day
|
||||||
|
@ -97,6 +106,9 @@ class _TemplateDayDetailRow extends StatelessWidget {
|
||||||
/// This causes a layered effect
|
/// This causes a layered effect
|
||||||
final bool isOdd;
|
final bool isOdd;
|
||||||
|
|
||||||
|
/// The index of the day
|
||||||
|
final int index;
|
||||||
|
|
||||||
/// The data of the day
|
/// The data of the day
|
||||||
final DayTemplateDataViewModel? dayData;
|
final DayTemplateDataViewModel? dayData;
|
||||||
|
|
||||||
|
@ -107,6 +119,7 @@ class _TemplateDayDetailRow extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
var startTime = dayData?.startTime;
|
var startTime = dayData?.startTime;
|
||||||
var endTime = dayData?.endTime;
|
var endTime = dayData?.endTime;
|
||||||
|
@ -119,6 +132,8 @@ class _TemplateDayDetailRow extends StatelessWidget {
|
||||||
dayPeriod = translations.unavailable;
|
dayPeriod = translations.unavailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dayPeriodIdentifier = "${identifiers.weekDayTimeIdentifier}_$index";
|
||||||
|
|
||||||
var breaks = dayData?.breaks ?? <BreakViewModel>[];
|
var breaks = dayData?.breaks ?? <BreakViewModel>[];
|
||||||
|
|
||||||
BoxDecoration? boxDecoration;
|
BoxDecoration? boxDecoration;
|
||||||
|
@ -145,13 +160,23 @@ class _TemplateDayDetailRow extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(dayName, style: textTheme.bodyLarge),
|
Text(dayName, style: textTheme.bodyLarge),
|
||||||
Text(dayPeriod, style: textTheme.bodyLarge),
|
CustomSemantics(
|
||||||
|
identifier: dayPeriodIdentifier,
|
||||||
|
child: Text(
|
||||||
|
dayPeriod,
|
||||||
|
style: textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// for each break add a line
|
// for each break add a line
|
||||||
for (var dayBreak in breaks) ...[
|
for (var (breakIndex, dayBreak) in breaks.indexed) ...[
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
_TemplateDayDetailPauseRow(dayBreakViewModel: dayBreak),
|
_TemplateDayDetailPauseRow(
|
||||||
|
dayBreakViewModel: dayBreak,
|
||||||
|
dayIndex: index,
|
||||||
|
breakIndex: breakIndex,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -162,10 +187,20 @@ class _TemplateDayDetailRow extends StatelessWidget {
|
||||||
class _TemplateDayDetailPauseRow extends StatelessWidget {
|
class _TemplateDayDetailPauseRow extends StatelessWidget {
|
||||||
const _TemplateDayDetailPauseRow({
|
const _TemplateDayDetailPauseRow({
|
||||||
required this.dayBreakViewModel,
|
required this.dayBreakViewModel,
|
||||||
|
required this.dayIndex,
|
||||||
|
required this.breakIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
final BreakViewModel dayBreakViewModel;
|
final BreakViewModel dayBreakViewModel;
|
||||||
|
|
||||||
|
/// The index of the day in the list of days
|
||||||
|
/// This is used to create unique identifiers when there are multiple days
|
||||||
|
/// with breaks
|
||||||
|
final int dayIndex;
|
||||||
|
|
||||||
|
/// The index of the break in the list of breaks
|
||||||
|
final int breakIndex;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
|
@ -173,6 +208,7 @@ class _TemplateDayDetailPauseRow extends StatelessWidget {
|
||||||
var availabilityScope = AvailabilityScope.of(context);
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
var options = availabilityScope.options;
|
var options = availabilityScope.options;
|
||||||
var translations = options.translations;
|
var translations = options.translations;
|
||||||
|
var identifiers = options.accessibilityIds;
|
||||||
|
|
||||||
var dayBreak = dayBreakViewModel.toBreak();
|
var dayBreak = dayBreakViewModel.toBreak();
|
||||||
var startTime = TimeOfDay.fromDateTime(dayBreak.startTime);
|
var startTime = TimeOfDay.fromDateTime(dayBreak.startTime);
|
||||||
|
@ -186,6 +222,9 @@ class _TemplateDayDetailPauseRow extends StatelessWidget {
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var breakIdentifier =
|
||||||
|
"${identifiers.weekDayBreakIdentifier}_${dayIndex}_$breakIndex";
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
@ -196,10 +235,13 @@ class _TemplateDayDetailPauseRow extends StatelessWidget {
|
||||||
style: pauseTextStyle,
|
style: pauseTextStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
CustomSemantics(
|
||||||
|
identifier: breakIdentifier,
|
||||||
|
child: Text(
|
||||||
pausePeriod,
|
pausePeriod,
|
||||||
style: pauseTextStyle,
|
style: pauseTextStyle,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: flutter_availability
|
name: flutter_availability
|
||||||
description: "Flutter availability userstory package"
|
description: "Flutter availability userstory package"
|
||||||
version: 1.0.0
|
version: 1.1.0
|
||||||
|
|
||||||
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ dependencies:
|
||||||
flutter_hooks: ^0.20.5
|
flutter_hooks: ^0.20.5
|
||||||
flutter_availability_data_interface:
|
flutter_availability_data_interface:
|
||||||
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
version: ^1.0.0
|
version: ^1.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -25,4 +25,3 @@ dev_dependencies:
|
||||||
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
ref: 7.0.0
|
ref: 7.0.0
|
||||||
|
|
||||||
flutter:
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: flutter_availability_data_interface
|
name: flutter_availability_data_interface
|
||||||
description: "The data interface for the flutter_availability component"
|
description: "The data interface for the flutter_availability component"
|
||||||
version: 1.0.0
|
version: 1.1.0
|
||||||
|
|
||||||
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue