fix: add NavigatorPopHandler with a custom PopHandler class for handling backarrow presses in the userstory

This commit is contained in:
Freek van de Ven 2024-07-25 14:51:10 +02:00
parent 26d2eb150a
commit c20a1a603c
9 changed files with 85 additions and 14 deletions

View file

@ -0,0 +1,24 @@
import "package:flutter/material.dart";
///
class PopHandler {
/// Constructor
PopHandler();
final List<VoidCallback> _handlers = [];
/// Registers a new handler
void add(VoidCallback handler) {
_handlers.add(handler);
}
/// Removes a handler
void remove(VoidCallback handler) {
_handlers.remove(handler);
}
/// Handles the pop
void handlePop() {
_handlers.lastOrNull?.call();
}
}

View file

@ -9,11 +9,12 @@ 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";
import "package:flutter_hooks/flutter_hooks.dart";
/// Screen for modifying the availabilities for a specific daterange /// Screen for modifying the availabilities for a specific daterange
/// There might already be availabilities for the selected period but they /// There might already be availabilities for the selected period but they
/// will be overwritten /// will be overwritten
class AvailabilitiesModificationScreen extends StatefulWidget { class AvailabilitiesModificationScreen extends StatefulHookWidget {
/// Constructor /// Constructor
const AvailabilitiesModificationScreen({ const AvailabilitiesModificationScreen({
required this.dateRange, required this.dateRange,
@ -62,6 +63,11 @@ class _AvailabilitiesModificationScreenState
var spacing = options.spacing; var spacing = options.spacing;
var translations = options.translations; var translations = options.translations;
useEffect(() {
availabilityScope.popHandler.add(widget.onExit);
return () => availabilityScope.popHandler.remove(widget.onExit);
});
// TODO(freek): the selected period might be longer than 1 month // TODO(freek): the selected period might be longer than 1 month
//so we need to get all the availabilites through a stream //so we need to get all the availabilites through a stream
Future<void> onSave() async { Future<void> onSave() async {
@ -180,7 +186,7 @@ class _AvailabilitiesModificationScreenState
} }
} }
class _AvailabilitiesModificationScreenLayout extends StatelessWidget { class _AvailabilitiesModificationScreenLayout extends HookWidget {
const _AvailabilitiesModificationScreenLayout({ const _AvailabilitiesModificationScreenLayout({
required this.dateRange, required this.dateRange,
required this.clearAvailability, required this.clearAvailability,

View file

@ -17,7 +17,7 @@ class AvailabilityOverview extends StatefulHookWidget {
}); });
/// Callback for when the user gives an availability range /// Callback for when the user gives an availability range
final void Function( final Future<void> Function(
DateTimeRange range, DateTimeRange range,
List<AvailabilityWithTemplate> selectedAvailabilities, List<AvailabilityWithTemplate> selectedAvailabilities,
) onEditDateRange; ) onEditDateRange;
@ -49,6 +49,11 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
[_selectedDate], [_selectedDate],
); );
useEffect(() {
availabilityScope.popHandler.add(widget.onExit);
return () => availabilityScope.popHandler.remove(widget.onExit);
});
var availabilitySnapshot = useStream(availabilityStream); var availabilitySnapshot = useStream(availabilityStream);
var selectedAvailabilities = [ var selectedAvailabilities = [
@ -94,14 +99,16 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
VoidCallback? onButtonPress; VoidCallback? onButtonPress;
if (_selectedRange != null) { if (_selectedRange != null) {
onButtonPress = () { onButtonPress = () async {
widget.onEditDateRange( await widget.onEditDateRange(
_selectedRange!, _selectedRange!,
selectedAvailabilities, selectedAvailabilities,
); );
if (mounted) {
setState(() { setState(() {
_selectedRange = null; _selectedRange = null;
}); });
}
}; };
} }

View file

@ -7,9 +7,10 @@ import "package:flutter_availability/src/ui/widgets/template_name_input.dart";
import "package:flutter_availability/src/ui/widgets/template_time_break.dart"; import "package:flutter_availability/src/ui/widgets/template_time_break.dart";
import "package:flutter_availability/src/util/scope.dart"; import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart"; import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
import "package:flutter_hooks/flutter_hooks.dart";
/// Page for creating or editing a day template /// Page for creating or editing a day template
class DayTemplateModificationScreen extends StatefulWidget { class DayTemplateModificationScreen extends StatefulHookWidget {
/// Constructor /// Constructor
const DayTemplateModificationScreen({ const DayTemplateModificationScreen({
required this.template, required this.template,
@ -50,6 +51,11 @@ class _DayTemplateModificationScreenState
var options = availabilityScope.options; var options = availabilityScope.options;
var translations = options.translations; var translations = options.translations;
useEffect(() {
availabilityScope.popHandler.add(widget.onExit);
return () => availabilityScope.popHandler.remove(widget.onExit);
});
Future<void> onDeletePressed() async { Future<void> onDeletePressed() async {
var isConfirmed = await options.confirmationDialogBuilder( var isConfirmed = await options.confirmationDialogBuilder(
context, context,

View file

@ -41,6 +41,11 @@ class AvailabilityTemplateOverview extends HookWidget {
var dayTemplatesSnapshot = useStream(dayTemplateStream); var dayTemplatesSnapshot = useStream(dayTemplateStream);
var weekTemplatesSnapshot = useStream(weekTemplateStream); var weekTemplatesSnapshot = useStream(weekTemplateStream);
useEffect(() {
availabilityScope.popHandler.add(onExit);
return () => availabilityScope.popHandler.remove(onExit);
});
var dayTemplates = var dayTemplates =
dayTemplatesSnapshot.data ?? <AvailabilityTemplateModel>[]; dayTemplatesSnapshot.data ?? <AvailabilityTemplateModel>[];
var weekTemplates = var weekTemplates =

View file

@ -8,9 +8,10 @@ import "package:flutter_availability/src/ui/widgets/template_week_day_selection.
import "package:flutter_availability/src/ui/widgets/template_week_overview.dart"; import "package:flutter_availability/src/ui/widgets/template_week_overview.dart";
import "package:flutter_availability/src/util/scope.dart"; import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart"; import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
import "package:flutter_hooks/flutter_hooks.dart";
/// Page for creating or editing a day template /// Page for creating or editing a day template
class WeekTemplateModificationScreen extends StatefulWidget { class WeekTemplateModificationScreen extends StatefulHookWidget {
/// Constructor /// Constructor
const WeekTemplateModificationScreen({ const WeekTemplateModificationScreen({
required this.template, required this.template,
@ -111,6 +112,11 @@ class _WeekTemplateModificationScreenState
widget.onExit(); widget.onExit();
} }
useEffect(() {
availabilityScope.popHandler.add(onBackPressed);
return () => availabilityScope.popHandler.remove(onBackPressed);
});
var canSave = _viewModel.canSave; var canSave = _viewModel.canSave;
var nextButton = options.primaryButtonBuilder( var nextButton = options.primaryButtonBuilder(
context, context,

View file

@ -1,6 +1,7 @@
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_availability/flutter_availability.dart"; import "package:flutter_availability/flutter_availability.dart";
import "package:flutter_availability/src/service/availability_service.dart"; import "package:flutter_availability/src/service/availability_service.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";
import "package:flutter_availability/src/ui/widgets/generic_time_selection.dart"; import "package:flutter_availability/src/ui/widgets/generic_time_selection.dart";
import "package:flutter_availability/src/ui/widgets/input_fields.dart"; import "package:flutter_availability/src/ui/widgets/input_fields.dart";
@ -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 popHandler = availabilityScope.popHandler;
Future<BreakViewModel?> openBreakDialog( Future<BreakViewModel?> openBreakDialog(
BreakViewModel? initialBreak, BreakViewModel? initialBreak,
@ -43,6 +45,7 @@ class PauseSelection extends StatelessWidget {
userId: availabilityScope.userId, userId: availabilityScope.userId,
options: options, options: options,
service: availabilityScope.service, service: availabilityScope.service,
popHandler: popHandler,
editingTemplate: editingTemplate, editingTemplate: editingTemplate,
); );
} }
@ -201,6 +204,7 @@ class AvailabilityBreakSelectionDialog extends StatefulWidget {
required AvailabilityOptions options, required AvailabilityOptions options,
required String userId, required String userId,
required AvailabilityService service, required AvailabilityService service,
required PopHandler popHandler,
required bool editingTemplate, required bool editingTemplate,
BreakViewModel? initialBreak, BreakViewModel? initialBreak,
}) async => }) async =>
@ -217,6 +221,7 @@ class AvailabilityBreakSelectionDialog extends StatefulWidget {
userId: userId, userId: userId,
options: options, options: options,
service: service, service: service,
popHandler: popHandler,
child: AvailabilityBreakSelectionDialog( child: AvailabilityBreakSelectionDialog(
initialBreak: initialBreak, initialBreak: initialBreak,
editingTemplate: editingTemplate, editingTemplate: editingTemplate,

View file

@ -2,6 +2,7 @@ import "package:flutter/material.dart";
import "package:flutter_availability/src/config/availability_options.dart"; import "package:flutter_availability/src/config/availability_options.dart";
import "package:flutter_availability/src/routes.dart"; import "package:flutter_availability/src/routes.dart";
import "package:flutter_availability/src/service/availability_service.dart"; import "package:flutter_availability/src/service/availability_service.dart";
import "package:flutter_availability/src/service/pop_handler.dart";
import "package:flutter_availability/src/util/scope.dart"; import "package:flutter_availability/src/util/scope.dart";
/// This pushes the availability user story to the navigator stack. /// This pushes the availability user story to the navigator stack.
@ -58,16 +59,22 @@ class _AvailabilityUserStoryState extends State<AvailabilityUserStory> {
dataInterface: widget.options.dataInterface, dataInterface: widget.options.dataInterface,
); );
late final PopHandler _popHandler = PopHandler();
@override @override
Widget build(BuildContext context) => AvailabilityScope( Widget build(BuildContext context) => AvailabilityScope(
userId: widget.userId, userId: widget.userId,
options: widget.options, options: widget.options,
service: _service, service: _service,
popHandler: _popHandler,
child: NavigatorPopHandler(
onPop: _popHandler.handlePop,
child: Navigator( child: Navigator(
onGenerateInitialRoutes: (state, route) => [ onGenerateInitialRoutes: (state, route) => [
homePageRoute(widget.onExit ?? () {}), homePageRoute(widget.onExit ?? () {}),
], ],
), ),
),
); );
@override @override

View file

@ -1,6 +1,7 @@
import "package:flutter/widgets.dart"; import "package:flutter/widgets.dart";
import "package:flutter_availability/src/config/availability_options.dart"; import "package:flutter_availability/src/config/availability_options.dart";
import "package:flutter_availability/src/service/availability_service.dart"; import "package:flutter_availability/src/service/availability_service.dart";
import "package:flutter_availability/src/service/pop_handler.dart";
/// ///
class AvailabilityScope extends InheritedWidget { class AvailabilityScope extends InheritedWidget {
@ -9,6 +10,7 @@ class AvailabilityScope extends InheritedWidget {
required this.userId, required this.userId,
required this.options, required this.options,
required this.service, required this.service,
required this.popHandler,
required super.child, required super.child,
super.key, super.key,
}); });
@ -22,6 +24,9 @@ class AvailabilityScope extends InheritedWidget {
/// ///
final AvailabilityService service; final AvailabilityService service;
///
final PopHandler popHandler;
@override @override
bool updateShouldNotify(AvailabilityScope oldWidget) => bool updateShouldNotify(AvailabilityScope oldWidget) =>
oldWidget.userId != userId || options != options; oldWidget.userId != userId || options != options;