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_clear.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/availability_template_selection.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/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/ui/widgets/pause_selection.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";
|
||||||
|
@ -127,111 +128,156 @@ class _AvailabilitiesModificationScreenState
|
||||||
Text(translations.saveButton),
|
Text(translations.saveButton),
|
||||||
);
|
);
|
||||||
|
|
||||||
var clearSection = AvailabilityClearSection(
|
// ignore: avoid_positional_boolean_parameters
|
||||||
range: widget.dateRange,
|
void onClearSection(bool isChecked) {
|
||||||
clearAvailable: _clearAvailability,
|
|
||||||
// TODO(Joey): Extract this function
|
|
||||||
onChanged: (isChecked) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_clearAvailability = isChecked;
|
_clearAvailability = isChecked;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
var templateSelection = AvailabilityTemplateSelection(
|
Future<void> onTemplateSelected() async {
|
||||||
selectedTemplates: _selectedTemplates,
|
|
||||||
// TODO(Joey): Extract this function
|
|
||||||
onTemplateAdd: () async {
|
|
||||||
var template = await widget.onTemplateSelection();
|
var template = await widget.onTemplateSelection();
|
||||||
if (template != null) {
|
if (template != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedTemplates = [template];
|
_selectedTemplates = [template];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// TODO(Joey): Extract these functions
|
|
||||||
onTemplatesRemoved: () {
|
void onTemplatesRemoved() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedTemplates = [];
|
_selectedTemplates = [];
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
var timeSelection = AvailabilityTimeSelection(
|
void onStartChanged(TimeOfDay start) {
|
||||||
dateRange: widget.dateRange,
|
setState(() {
|
||||||
startTime: _startTime,
|
|
||||||
endTime: _endTime,
|
|
||||||
key: ValueKey([_startTime, _endTime]),
|
|
||||||
// TODO(Joey): Extract these
|
|
||||||
onStartChanged: (start) => setState(() {
|
|
||||||
_startTime = start;
|
_startTime = start;
|
||||||
}),
|
});
|
||||||
// TODO(Joey): Extract these
|
}
|
||||||
onEndChanged: (end) => setState(() {
|
|
||||||
_endTime = end;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
var pauseSelection = PauseSelection(
|
void onEndChanged(TimeOfDay end) {
|
||||||
breaks: _availability.breaks
|
setState(() {
|
||||||
.map(BreakViewModel.fromAvailabilityBreakModel)
|
_endTime = end;
|
||||||
.toList(),
|
});
|
||||||
editingTemplate: false,
|
}
|
||||||
// TODO(Joey): Extract these
|
|
||||||
onBreaksChanged: (breaks) {
|
void onBreaksChanged(List<BreakViewModel> breaks) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_availability = _availability.copyWith(
|
_availability = _availability.copyWith(
|
||||||
breaks: breaks.map((b) => b.toBreak()).toList(),
|
breaks: breaks.map((b) => b.toBreak()).toList(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// TODO(Joey): this structure is defined multiple times, we should create
|
return _AvailabilitiesModificationScreenLayout(
|
||||||
// a widget to handle this consistently
|
dateRange: widget.dateRange,
|
||||||
var body = CustomScrollView(
|
clearAvailability: _clearAvailability,
|
||||||
slivers: [
|
onClearSection: onClearSection,
|
||||||
SliverPadding(
|
selectedTemplates: _selectedTemplates,
|
||||||
padding:
|
onTemplateSelected: onTemplateSelected,
|
||||||
EdgeInsets.symmetric(horizontal: options.spacing.sidePadding),
|
onTemplatesRemoved: onTemplatesRemoved,
|
||||||
sliver: SliverList.list(
|
startTime: _startTime,
|
||||||
children: [
|
endTime: _endTime,
|
||||||
const SizedBox(height: 40),
|
onStartChanged: onStartChanged,
|
||||||
clearSection,
|
onEndChanged: onEndChanged,
|
||||||
if (!_clearAvailability) ...[
|
breaks: _availability.breaks
|
||||||
const SizedBox(height: 24),
|
.map(BreakViewModel.fromAvailabilityBreakModel)
|
||||||
templateSelection,
|
.toList(),
|
||||||
const SizedBox(height: 24),
|
onBreaksChanged: onBreaksChanged,
|
||||||
timeSelection,
|
sidePadding: spacing.sidePadding,
|
||||||
// TODO(Joey): Not divisible by 4
|
bottomButtonPadding: spacing.bottomButtonPadding,
|
||||||
const SizedBox(height: 26),
|
saveButton: saveButton,
|
||||||
pauseSelection,
|
onExit: widget.onExit,
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SliverFillRemaining(
|
|
||||||
fillOverscroll: false,
|
|
||||||
hasScrollBody: false,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: spacing.sidePadding,
|
|
||||||
).copyWith(
|
|
||||||
bottom: spacing.bottomButtonPadding,
|
|
||||||
),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: saveButton,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
return options.baseScreenBuilder(
|
||||||
context,
|
context,
|
||||||
widget.onExit,
|
onExit,
|
||||||
body,
|
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/material.dart";
|
||||||
import "package:flutter_availability/src/service/availability_service.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/calendar.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/template_legend.dart";
|
import "package:flutter_availability/src/ui/widgets/template_legend.dart";
|
||||||
import "package:flutter_availability/src/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
@ -42,7 +43,6 @@ 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 spacing = options.spacing;
|
|
||||||
|
|
||||||
var availabilityStream = useMemoized(
|
var availabilityStream = useMemoized(
|
||||||
() => service.getOverviewDataForMonth(_selectedDate),
|
() => service.getOverviewDataForMonth(_selectedDate),
|
||||||
|
@ -51,18 +51,21 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
||||||
|
|
||||||
var availabilitySnapshot = useStream(availabilityStream);
|
var availabilitySnapshot = useStream(availabilityStream);
|
||||||
|
|
||||||
// TODO(Joey): Way too complex of a function
|
var selectedAvailabilities = <AvailabilityWithTemplate>[];
|
||||||
var selectedAvailabilities = _selectedRange != null
|
if (_selectedRange != null) {
|
||||||
? availabilitySnapshot.data
|
var availabilities = availabilitySnapshot.data
|
||||||
?.where(
|
?.where(
|
||||||
(a) =>
|
(a) =>
|
||||||
!a.availabilityModel.startDate
|
!a.availabilityModel.startDate
|
||||||
.isBefore(_selectedRange!.start) &&
|
.isBefore(_selectedRange!.start) &&
|
||||||
!a.availabilityModel.endDate.isAfter(_selectedRange!.end),
|
!a.availabilityModel.endDate.isAfter(_selectedRange!.end),
|
||||||
)
|
)
|
||||||
.toList() ??
|
.toList();
|
||||||
<AvailabilityWithTemplate>[]
|
|
||||||
: <AvailabilityWithTemplate>[];
|
if (availabilities != null) {
|
||||||
|
selectedAvailabilities = availabilities;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var availabilitiesAreSelected = selectedAvailabilities.isNotEmpty;
|
var availabilitiesAreSelected = selectedAvailabilities.isNotEmpty;
|
||||||
|
|
||||||
|
@ -94,10 +97,9 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
||||||
availabilities: availabilitySnapshot,
|
availabilities: availabilitySnapshot,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO(Joey): too complex of a definition for the function
|
VoidCallback? onButtonPress;
|
||||||
var onButtonPress = _selectedRange == null
|
if (_selectedRange != null) {
|
||||||
? null
|
onButtonPress = () {
|
||||||
: () {
|
|
||||||
widget.onEditDateRange(
|
widget.onEditDateRange(
|
||||||
_selectedRange!,
|
_selectedRange!,
|
||||||
selectedAvailabilities,
|
selectedAvailabilities,
|
||||||
|
@ -106,6 +108,7 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
||||||
_selectedRange = null;
|
_selectedRange = null;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> onClearButtonClicked() async {
|
Future<void> onClearButtonClicked() async {
|
||||||
var confirmed = await options.confirmationDialogBuilder(
|
var confirmed = await options.confirmationDialogBuilder(
|
||||||
|
@ -135,36 +138,18 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
|
||||||
Text(translations.editAvailabilityButton),
|
Text(translations.editAvailabilityButton),
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO(Joey): This structure is defined multiple times
|
return options.baseScreenBuilder(
|
||||||
var body = CustomScrollView(
|
context,
|
||||||
slivers: [
|
widget.onExit,
|
||||||
SliverPadding(
|
BasePage(
|
||||||
padding: EdgeInsets.symmetric(horizontal: spacing.sidePadding),
|
body: [
|
||||||
sliver: SliverList.list(
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
title,
|
title,
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
calendar,
|
calendar,
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
templateLegend,
|
templateLegend,
|
||||||
const SizedBox(height: 32),
|
|
||||||
],
|
],
|
||||||
),
|
buttons: [
|
||||||
),
|
|
||||||
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: [
|
|
||||||
startEditButton,
|
startEditButton,
|
||||||
if (availabilitiesAreSelected) ...[
|
if (availabilitiesAreSelected) ...[
|
||||||
const SizedBox(height: 8),
|
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/material.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/widgets/base_page.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
|
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/template_name_input.dart";
|
import "package:flutter_availability/src/ui/widgets/template_name_input.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/template_time_break.dart";
|
import "package:flutter_availability/src/ui/widgets/template_time_break.dart";
|
||||||
|
@ -47,7 +49,6 @@ 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 spacing = options.spacing;
|
|
||||||
|
|
||||||
Future<void> onDeletePressed() async {
|
Future<void> onDeletePressed() async {
|
||||||
await service.deleteTemplate(widget.template!);
|
await service.deleteTemplate(widget.template!);
|
||||||
|
@ -66,97 +67,69 @@ class _DayTemplateModificationScreenState
|
||||||
|
|
||||||
var canSave = _viewModel.canSave;
|
var canSave = _viewModel.canSave;
|
||||||
|
|
||||||
var saveButton = options.primaryButtonBuilder(
|
|
||||||
context,
|
|
||||||
canSave ? onSavePressed : null,
|
|
||||||
Text(translations.saveButton),
|
|
||||||
);
|
|
||||||
|
|
||||||
var deleteButton = options.bigTextButtonBuilder(
|
var deleteButton = options.bigTextButtonBuilder(
|
||||||
context,
|
context,
|
||||||
onDeletePressed,
|
onDeletePressed,
|
||||||
Text(translations.deleteTemplateButton),
|
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(
|
child: Text(
|
||||||
translations.dayTemplateTitle,
|
translations.dayTemplateTitle,
|
||||||
style: theme.textTheme.displaySmall,
|
style: theme.textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
var templateTitleSection = TemplateNameInput(
|
TemplateNameInput(
|
||||||
initialValue: _viewModel.name,
|
initialValue: _viewModel.name,
|
||||||
onNameChanged: (name) {
|
onNameChanged: onNameChanged,
|
||||||
setState(() {
|
),
|
||||||
_viewModel = _viewModel.copyWith(name: name);
|
const SizedBox(height: 24),
|
||||||
});
|
TemplateTimeAndBreakSection(
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
var colorSection = TemplateColorSelection(
|
|
||||||
selectedColor: _viewModel.color,
|
|
||||||
// TODO(Joey): Extract this
|
|
||||||
onColorSelected: (color) {
|
|
||||||
setState(() {
|
|
||||||
_viewModel = _viewModel.copyWith(color: color);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
var availabilitySection = TemplateTimeAndBreakSection(
|
|
||||||
dayData: _viewModel.data,
|
dayData: _viewModel.data,
|
||||||
onDayDataChanged: (data) {
|
onDayDataChanged: onDayDataChanged,
|
||||||
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,
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
templateTitleSection,
|
TemplateColorSelection(
|
||||||
const SizedBox(height: 24),
|
selectedColor: _viewModel.color,
|
||||||
availabilitySection,
|
onColorSelected: onColorSelected,
|
||||||
const SizedBox(height: 24),
|
),
|
||||||
colorSection,
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
],
|
],
|
||||||
|
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) ...[
|
if (widget.template != null) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
deleteButton,
|
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