Refactor excursion_main.dart to implement a confirmation dialog on page exit, allowing users to save templates or exit without saving. Update TrackingService to support instance reset for better state management.

This commit is contained in:
Nico
2025-06-04 22:19:58 +02:00
parent 4b88071700
commit ef5faf7d3d
2 changed files with 147 additions and 83 deletions

View File

@@ -458,96 +458,148 @@ class _ExcursionMainState extends State<ExcursionMain> {
),
];
// Begin of widget tree
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.excursion),
actions: [
// Text(TrackingService().isTracking ? "Tracking" : "Not tracking")
Image.asset(
TrackingService().isTracking ? "assets/icons/tracking_on.png" : "assets/icons/tracking_off.png",
width: 40,
),
],
),
body: PageTransitionSwitcher(
duration: const Duration(microseconds: 800),
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.vertical,
child: child,
);
},
child: Stepper(
key: ValueKey<int>(currentStep),
steps: getSteps(),
currentStep: currentStep,
onStepTapped: (value) {
setState(() {
currentStep = value;
});
return PopScope(
canPop: false,
onPopInvoked: (didPop) async {
if (didPop) {
return;
}
// Show confirmation dialog
final result = await showDialog<int>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Seite verlassen?'),
content: const Text('Möchten Sie die Seite wirklich verlassen?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(0),
child: const Text('Nein'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(1),
child: const Text('Ja und Template speichern'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(2),
child: const Text('Ja ohne Template'),
),
],
),
);
if (result == null || result == 0) {
return;
} else if (result == 1) {
// Save as template and leave
if (context.mounted) {
saveTemplate(
getFieldsText(),
DatabasesEnum.excursion,
);
}
TrackingService.resetInstance();
if (context.mounted) {
Navigator.of(context).pop();
}
} else {
// Just leave without saving
TrackingService.resetInstance();
if (context.mounted) {
Navigator.of(context).pop();
}
}
},
child: Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.excursion),
actions: [
// Text(TrackingService().isTracking ? "Tracking" : "Not tracking")
Image.asset(
TrackingService().isTracking ? "assets/icons/tracking_on.png" : "assets/icons/tracking_off.png",
width: 40,
),
],
),
body: PageTransitionSwitcher(
duration: const Duration(microseconds: 800),
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.vertical,
child: child,
);
},
onStepContinue: () async {
final isLastStep = currentStep == getSteps().length - 1;
if (!isLastStep) {
var res = await saveTemplate(
getFieldsText(),
DatabasesEnum.excursion,
);
isTemplate = true;
child: Stepper(
key: ValueKey<int>(currentStep),
steps: getSteps(),
currentStep: currentStep,
onStepTapped: (value) {
setState(() {
rmap["ID"]!["controller"]!.text = res.toString();
currentStep += 1;
currentStep = value;
});
} else {
if (widget.isSent) {
Navigator.pushNamedAndRemoveUntil(
context,
'/home',
(route) => false,
);
return;
}
},
onStepContinue: () async {
final isLastStep = currentStep == getSteps().length - 1;
bool empty = CheckRequired.checkRequired(rmap);
// for debugging always false
// empty = false;
if (empty) {
AddEntriesDialogHelper.showTemplateDialog(
context,
if (!isLastStep) {
var res = await saveTemplate(
getFieldsText(),
DatabasesEnum.excursion,
);
return;
isTemplate = true;
setState(() {
rmap["ID"]!["controller"]!.text = res.toString();
currentStep += 1;
});
} else {
bool pop = await AddEntriesDialogHelper.showSaveOptionsDialog(
context,
getFieldsText(),
widget.isTemplate,
DatabasesEnum.excursion,
);
if (pop && context.mounted) Navigator.of(context).pop();
if (widget.isSent) {
Navigator.pushNamedAndRemoveUntil(
context,
'/home',
(route) => false,
);
return;
}
bool empty = CheckRequired.checkRequired(rmap);
// for debugging always false
// empty = false;
if (empty) {
AddEntriesDialogHelper.showTemplateDialog(
context,
getFieldsText(),
DatabasesEnum.excursion,
);
return;
} else {
bool pop = await AddEntriesDialogHelper.showSaveOptionsDialog(
context,
getFieldsText(),
widget.isTemplate,
DatabasesEnum.excursion,
);
if (pop && context.mounted) Navigator.of(context).pop();
}
}
}
},
onStepCancel: () {
if (currentStep == 0) {
Navigator.pop(context);
} else {
setState(() {
currentStep -= 1;
});
}
},
},
onStepCancel: () {
if (currentStep == 0) {
Navigator.pop(context);
} else {
setState(() {
currentStep -= 1;
});
}
},
),
),
),
);

View File

@@ -9,10 +9,22 @@ import 'package:latlong2/latlong.dart';
import 'package:shared_preferences/shared_preferences.dart';
class TrackingService {
static final TrackingService _instance = TrackingService._internal();
factory TrackingService() => _instance;
static TrackingService? _instance;
factory TrackingService() {
_instance ??= TrackingService._internal();
return _instance!;
}
TrackingService._internal();
static void resetInstance() {
if (_instance != null) {
_instance!.dispose();
_instance = null;
}
}
List<LatLng> pathList = [];
List<double> accuracyList = [];
Timer? _positionTimer;