Skip to content

Commit

Permalink
fix: track unsaved changes on add-or-update-alarm-view (#466)
Browse files Browse the repository at this point in the history
* add initial listeners

* feat: add listeners to required variables
upgrade get version

* fix: add remaining listeners
refactor code

* refactor code
  • Loading branch information
keyurgit45 authored Mar 2, 2024
1 parent 351da7f commit a9e4230
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ class AddOrUpdateAlarmController extends GetxController {
RxBool isCustomSelected = false.obs;
RxBool isPlaying = false.obs; // Observable boolean to track playing state

// to check whether alarm data is updated or not
Map<String, dynamic> initialValues = {};
Map<String, dynamic> changedFields = {};

void toggleIsPlaying() {
isPlaying.toggle();
}
Expand Down Expand Up @@ -226,6 +230,80 @@ class AddOrUpdateAlarmController extends GetxController {
}
}

void checkUnsavedChangesAndNavigate(BuildContext context) {
int numberOfChangesMade =
changedFields.entries.where((element) => element.value == true).length;
if (numberOfChangesMade >= 1) {
Get.defaultDialog(
titlePadding: const EdgeInsets.symmetric(
vertical: 20,
),
backgroundColor: themeController.isLightMode.value
? kLightSecondaryBackgroundColor
: ksecondaryBackgroundColor,
title: 'Discard Changes?'.tr,
titleStyle: Theme.of(context).textTheme.displaySmall,
content: Column(
children: [
Text(
'unsavedChanges'.tr,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
Padding(
padding: const EdgeInsets.only(
top: 20,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(
onPressed: () {
Get.back();
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(kprimaryColor),
),
child: Text(
'Cancel'.tr,
style: Theme.of(context).textTheme.displaySmall!.copyWith(
color: kprimaryBackgroundColor,
),
),
),
OutlinedButton(
onPressed: () {
Get.back(closeOverlays: true);
Get.back();
},
style: OutlinedButton.styleFrom(
side: BorderSide(
color: themeController.isLightMode.value
? Colors.red.withOpacity(0.9)
: Colors.red,
width: 1,
),
),
child: Text(
'Leave'.tr,
style: Theme.of(context).textTheme.displaySmall!.copyWith(
color: themeController.isLightMode.value
? Colors.red.withOpacity(0.9)
: Colors.red,
),
),
),
],
),
),
],
),
);
} else {
Get.back();
}
}

Future<void> getLocation() async {
if (await _checkAndRequestPermission()) {
const timeLimit = Duration(seconds: 10);
Expand Down Expand Up @@ -693,6 +771,85 @@ class AddOrUpdateAlarmController extends GetxController {
repeatDays,
);

// store initial values of the variables
initialValues.addAll({
'selectedTime': selectedTime.value,
'daysRepeating': daysRepeating.value,
'snoozeDuration': snoozeDuration.value,
'deleteAfterGoesOff': deleteAfterGoesOff.value,
'label': label.value,
'note': note.value,
'customRingtoneName': customRingtoneName.value,
'volMin': volMin.value,
'volMax': volMax.value,
'gradient': gradient.value,
'showMotivationalQuote': showMotivationalQuote.value,
'activityInterval': activityInterval.value,
'weatherTypes': weatherTypes.value,
'location':
'${selectedPoint.value.latitude} ${selectedPoint.value.longitude}',
'shakeTimes': shakeTimes.value,
'qrValue': qrValue.value,
'mathsDifficulty': mathsDifficulty.value,
'mathsSliderValue': mathsSliderValue.value,
'numMathsQuestions': numMathsQuestions.value,
'numberOfSteps': numberOfSteps.value,
'isSharedAlarmEnabled': isSharedAlarmEnabled.value,
'offsetDuration': offsetDuration.value,
'isOffsetBefore': isOffsetBefore.value
});

addListeners();

if (await SecureStorageProvider().retrieveApiKey(ApiKeys.openWeatherMap) !=
null) {
weatherApiKeyExists.value = true;
}

// If there's an argument sent, we are in update mode
}

void addListeners() {
// Updating UI to show time to alarm
selectedTime.listen((time) {
debugPrint('CHANGED CHANGED CHANGED CHANGED');
timeToAlarm.value =
Utils.timeUntilAlarm(TimeOfDay.fromDateTime(time), repeatDays);
_compareAndSetChange('selectedTime', time);
});

//Updating UI to show repeated days
repeatDays.listen((days) {
daysRepeating.value = Utils.getRepeatDays(days);
_compareAndSetChange('daysRepeating', daysRepeating.value);
});

setupListener<int>(snoozeDuration, 'snoozeDuration');
setupListener<bool>(deleteAfterGoesOff, 'deleteAfterGoesOff');
setupListener<String>(label, 'label');
setupListener<String>(note, 'note');
setupListener<String>(customRingtoneName, 'customRingtoneName');
setupListener<double>(volMin, 'volMin');
setupListener<double>(volMax, 'volMax');
setupListener<int>(gradient, 'gradient');
setupListener<bool>(showMotivationalQuote, 'showMotivationalQuote');
setupListener<int>(activityInterval, 'activityInterval');

// Updating UI to show weather types
selectedWeather.listen((weather) {
if (weather.toList().isEmpty) {
isWeatherEnabled.value = false;
} else {
isWeatherEnabled.value = true;
}
weatherTypes.value = Utils.getFormattedWeatherTypes(weather);
_compareAndSetChange('weatherTypes', weatherTypes.value);
// if location based is disabled and weather based is disabled, reset location
if (weatherTypes.value == 'Off' && !isLocationEnabled.value) {
selectedPoint.value = LatLng(0, 0);
}
});

// Adding to markers list, to display on map
// (MarkersLayer takes only List<Marker>)
selectedPoint.listen(
Expand All @@ -709,38 +866,44 @@ class AddOrUpdateAlarmController extends GetxController {
),
),
);
_compareAndSetChange(
'location', '${point.latitude} ${point.longitude}');
},
);

// Updating UI to show time to alarm

selectedTime.listen((time) {
debugPrint('CHANGED CHANGED CHANGED CHANGED');
timeToAlarm.value =
Utils.timeUntilAlarm(TimeOfDay.fromDateTime(time), repeatDays);
// reset selectedPoint to default value if isLocationEnabled is false and weather based is off
isLocationEnabled.listen((value) {
if (!value && weatherTypes.value == 'Off') {
selectedPoint.value = LatLng(0, 0);
}
});

//Updating UI to show repeated days
repeatDays.listen((days) {
daysRepeating.value = Utils.getRepeatDays(days);
});
setupListener<int>(shakeTimes, 'shakeTimes');
setupListener<String>(qrValue, 'qrValue');
setupListener<double>(mathsSliderValue, 'mathsSliderValue');
setupListener<Difficulty>(mathsDifficulty, 'mathsDifficulty');
setupListener<int>(numMathsQuestions, 'numMathsQuestions');
setupListener<int>(numberOfSteps, 'numberOfSteps');

// Updating UI to show weather types
selectedWeather.listen((weather) {
if (weather.toList().isEmpty) {
isWeatherEnabled.value = false;
} else {
isWeatherEnabled.value = true;
}
weatherTypes.value = Utils.getFormattedWeatherTypes(weather);
setupListener<bool>(isSharedAlarmEnabled, 'isSharedAlarmEnabled');
setupListener<int>(offsetDuration, 'offsetDuration');
setupListener<bool>(isOffsetBefore, 'isOffsetBefore');
}

// adds listener to rxVar variable
void setupListener<T>(Rx<T> rxVar, String fieldName) {
rxVar.listen((value) {
_compareAndSetChange(fieldName, value);
});
}

if (await SecureStorageProvider().retrieveApiKey(ApiKeys.openWeatherMap) !=
null) {
weatherApiKeyExists.value = true;
// if initialValues map contains fieldName and newValue is equal to currentValue
// then set changeFields map field to true
void _compareAndSetChange(String fieldName, dynamic currentValue) {
if (initialValues.containsKey(fieldName)) {
bool hasChanged = initialValues[fieldName] != currentValue;
changedFields[fieldName] = hasChanged;
}

// If there's an argument sent, we are in update mode
}

@override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
Expand Down Expand Up @@ -48,79 +50,7 @@ class AddOrUpdateAlarmView extends GetView<AddOrUpdateAlarmController> {
if (didPop) {
return;
}

Get.defaultDialog(
titlePadding: const EdgeInsets.symmetric(
vertical: 20,
),
backgroundColor: themeController.isLightMode.value
? kLightSecondaryBackgroundColor
: ksecondaryBackgroundColor,
title: 'Discard Changes?'.tr,
titleStyle: Theme.of(context).textTheme.displaySmall,
content: Column(
children: [
Text(
'unsavedChanges'.tr,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
Padding(
padding: const EdgeInsets.only(
top: 20,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(
onPressed: () {
Get.back();
},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(kprimaryColor),
),
child: Text(
'Cancel'.tr,
style: Theme.of(context)
.textTheme
.displaySmall!
.copyWith(
color: kprimaryBackgroundColor,
),
),
),
OutlinedButton(
onPressed: () {
Get.back(closeOverlays: true);
Get.back();
},
style: OutlinedButton.styleFrom(
side: BorderSide(
color: themeController.isLightMode.value
? Colors.red.withOpacity(0.9)
: Colors.red,
width: 1,
),
),
child: Text(
'Leave'.tr,
style: Theme.of(context)
.textTheme
.displaySmall!
.copyWith(
color: themeController.isLightMode.value
? Colors.red.withOpacity(0.9)
: Colors.red,
),
),
),
],
),
),
],
),
);
controller.checkUnsavedChangesAndNavigate(context);
},
child: Scaffold(
floatingActionButtonLocation:
Expand Down
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -564,10 +564,10 @@ packages:
dependency: "direct main"
description:
name: get
sha256: "2ba20a47c8f1f233bed775ba2dd0d3ac97b4cf32fc17731b3dfc672b06b0e92a"
sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e
url: "https://pub.dev"
source: hosted
version: "4.6.5"
version: "4.6.6"
get_storage:
dependency: "direct main"
description:
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dependencies:
firebase_core: ^2.8.0
screen_state: ^2.0.0
cupertino_icons: ^1.0.2
get: 4.6.5
get: 4.6.6
get_storage: ^2.1.1
flutter_time_picker_spinner: ^2.0.0
cloud_firestore: ^4.4.5
Expand Down

0 comments on commit a9e4230

Please sign in to comment.