mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-19 13:13:44 +02:00
fix: use a BasePage to reduce code duplication in comparable layouts
This commit is contained in:
parent
d765a7a0f8
commit
a9f744d626
4 changed files with 298 additions and 249 deletions
|
@ -4,6 +4,7 @@ import "package:flutter_availability/src/ui/view_models/break_view_model.dart";
|
|||
import "package:flutter_availability/src/ui/widgets/availability_clear.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/availability_template_selection.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/availabillity_time_selection.dart";
|
||||
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/util/scope.dart";
|
||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||
|
@ -127,111 +128,156 @@ class _AvailabilitiesModificationScreenState
|
|||
Text(translations.saveButton),
|
||||
);
|
||||
|
||||
var clearSection = AvailabilityClearSection(
|
||||
range: widget.dateRange,
|
||||
clearAvailable: _clearAvailability,
|
||||
// TODO(Joey): Extract this function
|
||||
onChanged: (isChecked) {
|
||||
// ignore: avoid_positional_boolean_parameters
|
||||
void onClearSection(bool isChecked) {
|
||||
setState(() {
|
||||
_clearAvailability = isChecked;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
var templateSelection = AvailabilityTemplateSelection(
|
||||
selectedTemplates: _selectedTemplates,
|
||||
// TODO(Joey): Extract this function
|
||||
onTemplateAdd: () async {
|
||||
Future<void> onTemplateSelected() async {
|
||||
var template = await widget.onTemplateSelection();
|
||||
if (template != null) {
|
||||
setState(() {
|
||||
_selectedTemplates = [template];
|
||||
});
|
||||
}
|
||||
},
|
||||
// TODO(Joey): Extract these functions
|
||||
onTemplatesRemoved: () {
|
||||
}
|
||||
|
||||
void onTemplatesRemoved() {
|
||||
setState(() {
|
||||
_selectedTemplates = [];
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
var timeSelection = AvailabilityTimeSelection(
|
||||
dateRange: widget.dateRange,
|
||||
startTime: _startTime,
|
||||
endTime: _endTime,
|
||||
key: ValueKey([_startTime, _endTime]),
|
||||
// TODO(Joey): Extract these
|
||||
onStartChanged: (start) => setState(() {
|
||||
void onStartChanged(TimeOfDay start) {
|
||||
setState(() {
|
||||
_startTime = start;
|
||||
}),
|
||||
// TODO(Joey): Extract these
|
||||
onEndChanged: (end) => setState(() {
|
||||
_endTime = end;
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
var pauseSelection = PauseSelection(
|
||||
breaks: _availability.breaks
|
||||
.map(BreakViewModel.fromAvailabilityBreakModel)
|
||||
.toList(),
|
||||
editingTemplate: false,
|
||||
// TODO(Joey): Extract these
|
||||
onBreaksChanged: (breaks) {
|
||||
void onEndChanged(TimeOfDay end) {
|
||||
setState(() {
|
||||
_endTime = end;
|
||||
});
|
||||
}
|
||||
|
||||
void onBreaksChanged(List<BreakViewModel> breaks) {
|
||||
setState(() {
|
||||
_availability = _availability.copyWith(
|
||||
breaks: breaks.map((b) => b.toBreak()).toList(),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(Joey): this structure is defined multiple times, we should create
|
||||
// a widget to handle this consistently
|
||||
var body = CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: options.spacing.sidePadding),
|
||||
sliver: SliverList.list(
|
||||
children: [
|
||||
const SizedBox(height: 40),
|
||||
clearSection,
|
||||
if (!_clearAvailability) ...[
|
||||
const SizedBox(height: 24),
|
||||
templateSelection,
|
||||
const SizedBox(height: 24),
|
||||
timeSelection,
|
||||
// TODO(Joey): Not divisible by 4
|
||||
const SizedBox(height: 26),
|
||||
pauseSelection,
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
fillOverscroll: false,
|
||||
hasScrollBody: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: spacing.sidePadding,
|
||||
).copyWith(
|
||||
bottom: spacing.bottomButtonPadding,
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: saveButton,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
return _AvailabilitiesModificationScreenLayout(
|
||||
dateRange: widget.dateRange,
|
||||
clearAvailability: _clearAvailability,
|
||||
onClearSection: onClearSection,
|
||||
selectedTemplates: _selectedTemplates,
|
||||
onTemplateSelected: onTemplateSelected,
|
||||
onTemplatesRemoved: onTemplatesRemoved,
|
||||
startTime: _startTime,
|
||||
endTime: _endTime,
|
||||
onStartChanged: onStartChanged,
|
||||
onEndChanged: onEndChanged,
|
||||
breaks: _availability.breaks
|
||||
.map(BreakViewModel.fromAvailabilityBreakModel)
|
||||
.toList(),
|
||||
onBreaksChanged: onBreaksChanged,
|
||||
sidePadding: spacing.sidePadding,
|
||||
bottomButtonPadding: spacing.bottomButtonPadding,
|
||||
saveButton: saveButton,
|
||||
onExit: widget.onExit,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AvailabilitiesModificationScreenLayout extends StatelessWidget {
|
||||
const _AvailabilitiesModificationScreenLayout({
|
||||
required this.dateRange,
|
||||
required this.clearAvailability,
|
||||
required this.onClearSection,
|
||||
required this.selectedTemplates,
|
||||
required this.onTemplateSelected,
|
||||
required this.onTemplatesRemoved,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.onStartChanged,
|
||||
required this.onEndChanged,
|
||||
required this.breaks,
|
||||
required this.onBreaksChanged,
|
||||
required this.sidePadding,
|
||||
required this.bottomButtonPadding,
|
||||
required this.saveButton,
|
||||
required this.onExit,
|
||||
});
|
||||
|
||||
final DateTimeRange dateRange;
|
||||
final bool clearAvailability;
|
||||
// ignore: avoid_positional_boolean_parameters
|
||||
final void Function(bool isChecked) onClearSection;
|
||||
|
||||
final List<AvailabilityTemplateModel> selectedTemplates;
|
||||
final void Function() onTemplateSelected;
|
||||
final void Function() onTemplatesRemoved;
|
||||
|
||||
final TimeOfDay? startTime;
|
||||
final TimeOfDay? endTime;
|
||||
final void Function(TimeOfDay start) onStartChanged;
|
||||
final void Function(TimeOfDay start) onEndChanged;
|
||||
|
||||
final List<BreakViewModel> breaks;
|
||||
final void Function(List<BreakViewModel> breaks) onBreaksChanged;
|
||||
|
||||
final double sidePadding;
|
||||
final double bottomButtonPadding;
|
||||
|
||||
final Widget saveButton;
|
||||
|
||||
final VoidCallback onExit;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var availabilityScope = AvailabilityScope.of(context);
|
||||
var options = availabilityScope.options;
|
||||
|
||||
return options.baseScreenBuilder(
|
||||
context,
|
||||
widget.onExit,
|
||||
body,
|
||||
onExit,
|
||||
BasePage(
|
||||
body: [
|
||||
AvailabilityClearSection(
|
||||
range: dateRange,
|
||||
clearAvailable: clearAvailability,
|
||||
onChanged: onClearSection,
|
||||
),
|
||||
if (!clearAvailability) ...[
|
||||
const SizedBox(height: 24),
|
||||
AvailabilityTemplateSelection(
|
||||
selectedTemplates: selectedTemplates,
|
||||
onTemplateAdd: onTemplateSelected,
|
||||
onTemplatesRemoved: onTemplatesRemoved,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
AvailabilityTimeSelection(
|
||||
dateRange: dateRange,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
key: ValueKey([startTime, endTime]),
|
||||
onStartChanged: onStartChanged,
|
||||
onEndChanged: onEndChanged,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
PauseSelection(
|
||||
breaks: breaks,
|
||||
editingTemplate: false,
|
||||
onBreaksChanged: onBreaksChanged,
|
||||
),
|
||||
],
|
||||
],
|
||||
buttons: [saveButton],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/service/availability_service.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/template_legend.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
@ -42,7 +43,6 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
|||
var service = availabilityScope.service;
|
||||
var options = availabilityScope.options;
|
||||
var translations = options.translations;
|
||||
var spacing = options.spacing;
|
||||
|
||||
var availabilityStream = useMemoized(
|
||||
() => service.getOverviewDataForMonth(_selectedDate),
|
||||
|
@ -51,18 +51,21 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
|||
|
||||
var availabilitySnapshot = useStream(availabilityStream);
|
||||
|
||||
// TODO(Joey): Way too complex of a function
|
||||
var selectedAvailabilities = _selectedRange != null
|
||||
? availabilitySnapshot.data
|
||||
var selectedAvailabilities = <AvailabilityWithTemplate>[];
|
||||
if (_selectedRange != null) {
|
||||
var availabilities = availabilitySnapshot.data
|
||||
?.where(
|
||||
(a) =>
|
||||
!a.availabilityModel.startDate
|
||||
.isBefore(_selectedRange!.start) &&
|
||||
!a.availabilityModel.endDate.isAfter(_selectedRange!.end),
|
||||
)
|
||||
.toList() ??
|
||||
<AvailabilityWithTemplate>[]
|
||||
: <AvailabilityWithTemplate>[];
|
||||
.toList();
|
||||
|
||||
if (availabilities != null) {
|
||||
selectedAvailabilities = availabilities;
|
||||
}
|
||||
}
|
||||
|
||||
var availabilitiesAreSelected = selectedAvailabilities.isNotEmpty;
|
||||
|
||||
|
@ -94,10 +97,9 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
|||
availabilities: availabilitySnapshot,
|
||||
);
|
||||
|
||||
// TODO(Joey): too complex of a definition for the function
|
||||
var onButtonPress = _selectedRange == null
|
||||
? null
|
||||
: () {
|
||||
VoidCallback? onButtonPress;
|
||||
if (_selectedRange != null) {
|
||||
onButtonPress = () {
|
||||
widget.onEditDateRange(
|
||||
_selectedRange!,
|
||||
selectedAvailabilities,
|
||||
|
@ -106,6 +108,7 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
|||
_selectedRange = null;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> onClearButtonClicked() async {
|
||||
var confirmed = await options.confirmationDialogBuilder(
|
||||
|
@ -135,36 +138,18 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
|||
Text(translations.editAvailabilityButton),
|
||||
);
|
||||
|
||||
// TODO(Joey): This structure is defined multiple times
|
||||
var body = CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: spacing.sidePadding),
|
||||
sliver: SliverList.list(
|
||||
children: [
|
||||
const SizedBox(height: 40),
|
||||
return options.baseScreenBuilder(
|
||||
context,
|
||||
widget.onExit,
|
||||
BasePage(
|
||||
body: [
|
||||
title,
|
||||
const SizedBox(height: 24),
|
||||
calendar,
|
||||
const SizedBox(height: 32),
|
||||
templateLegend,
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
fillOverscroll: false,
|
||||
hasScrollBody: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: spacing.sidePadding,
|
||||
).copyWith(
|
||||
bottom: spacing.bottomButtonPadding,
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Column(
|
||||
children: [
|
||||
buttons: [
|
||||
startEditButton,
|
||||
if (availabilitiesAreSelected) ...[
|
||||
const SizedBox(height: 8),
|
||||
|
@ -172,16 +157,6 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
|||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return options.baseScreenBuilder(
|
||||
context,
|
||||
widget.onExit,
|
||||
body,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import "package:flutter/material.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/widgets/base_page.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/template_name_input.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/template_time_break.dart";
|
||||
|
@ -47,7 +49,6 @@ class _DayTemplateModificationScreenState
|
|||
var service = availabilityScope.service;
|
||||
var options = availabilityScope.options;
|
||||
var translations = options.translations;
|
||||
var spacing = options.spacing;
|
||||
|
||||
Future<void> onDeletePressed() async {
|
||||
await service.deleteTemplate(widget.template!);
|
||||
|
@ -66,97 +67,69 @@ class _DayTemplateModificationScreenState
|
|||
|
||||
var canSave = _viewModel.canSave;
|
||||
|
||||
var saveButton = options.primaryButtonBuilder(
|
||||
context,
|
||||
canSave ? onSavePressed : null,
|
||||
Text(translations.saveButton),
|
||||
);
|
||||
|
||||
var deleteButton = options.bigTextButtonBuilder(
|
||||
context,
|
||||
onDeletePressed,
|
||||
Text(translations.deleteTemplateButton),
|
||||
);
|
||||
|
||||
var title = Center(
|
||||
void onNameChanged(String name) {
|
||||
setState(() {
|
||||
_viewModel = _viewModel.copyWith(name: name);
|
||||
});
|
||||
}
|
||||
|
||||
void onColorSelected(int? color) {
|
||||
setState(() {
|
||||
_viewModel = _viewModel.copyWith(color: color);
|
||||
});
|
||||
}
|
||||
|
||||
void onDayDataChanged(DayTemplateDataViewModel data) {
|
||||
setState(() {
|
||||
_viewModel = _viewModel.copyWith(data: data);
|
||||
});
|
||||
}
|
||||
|
||||
return options.baseScreenBuilder(
|
||||
context,
|
||||
widget.onExit,
|
||||
BasePage(
|
||||
body: [
|
||||
Center(
|
||||
child: Text(
|
||||
translations.dayTemplateTitle,
|
||||
style: theme.textTheme.displaySmall,
|
||||
),
|
||||
);
|
||||
|
||||
var templateTitleSection = TemplateNameInput(
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
TemplateNameInput(
|
||||
initialValue: _viewModel.name,
|
||||
onNameChanged: (name) {
|
||||
setState(() {
|
||||
_viewModel = _viewModel.copyWith(name: name);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
var colorSection = TemplateColorSelection(
|
||||
selectedColor: _viewModel.color,
|
||||
// TODO(Joey): Extract this
|
||||
onColorSelected: (color) {
|
||||
setState(() {
|
||||
_viewModel = _viewModel.copyWith(color: color);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
var availabilitySection = TemplateTimeAndBreakSection(
|
||||
onNameChanged: onNameChanged,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
TemplateTimeAndBreakSection(
|
||||
dayData: _viewModel.data,
|
||||
onDayDataChanged: (data) {
|
||||
setState(() {
|
||||
_viewModel = _viewModel.copyWith(data: data);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
var body = CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: spacing.sidePadding),
|
||||
sliver: SliverList.list(
|
||||
children: [
|
||||
const SizedBox(height: 40),
|
||||
title,
|
||||
onDayDataChanged: onDayDataChanged,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
templateTitleSection,
|
||||
const SizedBox(height: 24),
|
||||
availabilitySection,
|
||||
const SizedBox(height: 24),
|
||||
colorSection,
|
||||
const SizedBox(height: 32),
|
||||
TemplateColorSelection(
|
||||
selectedColor: _viewModel.color,
|
||||
onColorSelected: onColorSelected,
|
||||
),
|
||||
],
|
||||
buttons: [
|
||||
options.primaryButtonBuilder(
|
||||
context,
|
||||
canSave ? onSavePressed : null,
|
||||
Text(translations.saveButton),
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
fillOverscroll: false,
|
||||
hasScrollBody: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: spacing.sidePadding,
|
||||
).copyWith(
|
||||
bottom: spacing.bottomButtonPadding,
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Column(
|
||||
children: [
|
||||
saveButton,
|
||||
if (widget.template != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
deleteButton,
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return options.baseScreenBuilder(context, widget.onExit, body);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_availability/src/util/scope.dart";
|
||||
|
||||
///
|
||||
class BasePage extends StatelessWidget {
|
||||
///
|
||||
const BasePage({
|
||||
required this.body,
|
||||
required this.buttons,
|
||||
super.key,
|
||||
});
|
||||
|
||||
///
|
||||
final List<Widget> body;
|
||||
|
||||
///
|
||||
final List<Widget> buttons;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var availabilityScope = AvailabilityScope.of(context);
|
||||
var options = availabilityScope.options;
|
||||
var spacing = options.spacing;
|
||||
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: spacing.sidePadding),
|
||||
sliver: SliverList.list(
|
||||
children: [
|
||||
const SizedBox(height: 40),
|
||||
...body,
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
fillOverscroll: false,
|
||||
hasScrollBody: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: spacing.sidePadding,
|
||||
).copyWith(
|
||||
bottom: spacing.bottomButtonPadding,
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Column(children: buttons),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue