mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-19 13:13:44 +02:00
Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
|
79d292cf4a | ||
|
9487cf2e57 | ||
|
bf591a45a2 | ||
|
38a9351bbb | ||
|
78acd0e41a | ||
|
4247cc6ba9 | ||
|
bd8bc994f8 |
26 changed files with 810 additions and 311 deletions
2
.fvmrc
2
.fvmrc
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"flutter": "3.22.2"
|
"flutter": "3.24.3"
|
||||||
}
|
}
|
1
.github/workflows/melos-ci.yml
vendored
1
.github/workflows/melos-ci.yml
vendored
|
@ -12,3 +12,4 @@ jobs:
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
with:
|
with:
|
||||||
subfolder: '.' # add optional subfolder to run workflow in
|
subfolder: '.' # add optional subfolder to run workflow in
|
||||||
|
flutter_version: 3.24.3
|
||||||
|
|
14
.github/workflows/release.yml
vendored
Normal file
14
.github/workflows/release.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name: Iconica Standard Component Release Workflow
|
||||||
|
# Workflow Caller version: 1.0.0
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-global-iconica-workflow:
|
||||||
|
uses: Iconica-Development/.github/.github/workflows/component-release.yml@master
|
||||||
|
secrets: inherit
|
||||||
|
permissions: write-all
|
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -1,3 +1,15 @@
|
||||||
|
## 1.2.0
|
||||||
|
|
||||||
|
* Improve the UI for smaller screens to prevent overflows
|
||||||
|
|
||||||
|
## 1.1.1
|
||||||
|
|
||||||
|
* Removed custom definition of CustomSemantics to use the one from flutter_accessibility instead
|
||||||
|
|
||||||
|
## 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;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.dart";
|
||||||
import "package:flutter_availability/flutter_availability.dart";
|
import "package:flutter_availability/flutter_availability.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/availability_view_model.dart";
|
import "package:flutter_availability/src/ui/view_models/availability_view_model.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";
|
||||||
|
@ -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,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.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/template_legend.dart";
|
import "package:flutter_availability/src/ui/widgets/template_legend.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(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.dart";
|
||||||
import "package:flutter_availability/src/service/errors.dart";
|
import "package:flutter_availability/src/service/errors.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/day_template_view_model.dart";
|
import "package:flutter_availability/src/ui/view_models/day_template_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";
|
||||||
|
@ -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,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.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/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 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 {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.dart";
|
||||||
import "package:flutter_availability/src/service/errors.dart";
|
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";
|
||||||
|
@ -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_accessibility/flutter_accessibility.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,11 +68,14 @@ class AvailabilityClearSection extends StatelessWidget {
|
||||||
onChanged(value);
|
onChanged(value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
unavailableText,
|
unavailableText,
|
||||||
style: textTheme.bodyMedium,
|
style: textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.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,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.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/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
@ -65,45 +66,60 @@ 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(
|
||||||
(element) => element.templateDeviation,
|
(element) => element.templateDeviation,
|
||||||
);
|
);
|
||||||
|
|
||||||
var monthDateSelector = Row(
|
var monthDateSelector = LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
var monthWidth =
|
||||||
|
_calculateTextWidthOfLongestMonth(context, translations);
|
||||||
|
var sideSpace =
|
||||||
|
((constraints.maxWidth - monthWidth) / 2 - 44).clamp(0.0, 44.0);
|
||||||
|
|
||||||
|
return 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: () {
|
||||||
onMonthChanged(
|
onMonthChanged(DateTime(month.year, month.month - 1));
|
||||||
DateTime(month.year, month.month - 1),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 44),
|
),
|
||||||
|
SizedBox(width: sideSpace),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: _calculateTextWidthOfLongestMonth(context, translations),
|
width: monthWidth,
|
||||||
|
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),
|
),
|
||||||
IconButton(
|
SizedBox(width: sideSpace),
|
||||||
|
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: () {
|
||||||
onMonthChanged(
|
onMonthChanged(DateTime(month.year, month.month + 1));
|
||||||
DateTime(month.year, month.month + 1),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
var calendarGrid = CalendarGrid(
|
var calendarGrid = CalendarGrid(
|
||||||
month: month,
|
month: month,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.dart";
|
||||||
import "package:flutter_availability/flutter_availability.dart";
|
import "package:flutter_availability/flutter_availability.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -159,9 +167,12 @@ class _CalendarDay extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
decoration: decoration,
|
decoration: decoration,
|
||||||
child: Text(day.date.day.toString(), style: textStyle),
|
child: Text(
|
||||||
|
day.date.day.toString(),
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (day.templateDeviation) ...[
|
if (day.templateDeviation) ...[
|
||||||
|
@ -173,6 +184,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_accessibility/flutter_accessibility.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_accessibility/flutter_accessibility.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,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.dart";
|
||||||
import "package:flutter_availability/flutter_availability.dart";
|
import "package:flutter_availability/flutter_availability.dart";
|
||||||
import "package:flutter_availability/src/service/pop_handler.dart";
|
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";
|
||||||
|
@ -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,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.dart";
|
||||||
import "package:flutter_availability/src/config/availability_options.dart";
|
import "package:flutter_availability/src/config/availability_options.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,
|
||||||
|
@ -72,13 +76,16 @@ class _TemplateLegendState extends State<TemplateLegend> {
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
const Icon(Icons.add, size: 20),
|
const Icon(Icons.add, size: 20),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
translations.createTemplateButton,
|
translations.createTemplateButton,
|
||||||
style: textTheme.bodyLarge,
|
style: textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget body = const Divider(
|
Widget body = const Divider(
|
||||||
|
@ -104,6 +111,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 +124,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 +132,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 +159,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 +185,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 +221,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 +241,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 +260,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_accessibility/flutter_accessibility.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,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// ignore_for_file: avoid_positional_boolean_parameters
|
// ignore_for_file: avoid_positional_boolean_parameters
|
||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.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/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
|
||||||
|
@ -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,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_accessibility/flutter_accessibility.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/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";
|
||||||
|
@ -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,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ class AvailabilityScope extends InheritedWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(AvailabilityScope oldWidget) =>
|
bool updateShouldNotify(AvailabilityScope oldWidget) =>
|
||||||
oldWidget.userId != userId || options != options;
|
oldWidget.userId != userId || oldWidget.options != options;
|
||||||
|
|
||||||
///
|
///
|
||||||
static AvailabilityScope of(BuildContext context) =>
|
static AvailabilityScope of(BuildContext context) =>
|
||||||
|
|
|
@ -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.2.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,10 @@ 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.2.0
|
||||||
|
flutter_accessibility:
|
||||||
|
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
|
version: ^0.0.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -25,4 +28,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.2.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