-
Notifications
You must be signed in to change notification settings - Fork 149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: System Theme Mode #451
Changes from 2 commits
f6d4f29
4b0837c
2e69c97
8295f3c
74c47f4
98848f9
cd5a2cf
b8db0cf
717048c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,100 @@ | ||
import 'dart:async'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:get/get.dart'; | ||
import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart'; | ||
import 'package:ultimate_alarm_clock/app/utils/constants.dart'; | ||
import 'dart:developer' as dev; | ||
|
||
class ThemeController extends GetxController { | ||
var isLightMode = true.obs; | ||
final _secureStorageProvider = SecureStorageProvider(); | ||
|
||
late Timer _brightnessCheckTimer; | ||
bool isSystemModeActive = false; | ||
|
||
@override | ||
void onInit() { | ||
_loadThemeValue(); | ||
super.onInit(); | ||
} | ||
|
||
@override | ||
void onClose() { | ||
// _brightnessCheckTimer.cancel(); | ||
super.onClose(); | ||
} | ||
|
||
void _loadThemeValue() async { | ||
isLightMode.value = | ||
await _secureStorageProvider.readThemeValue() == AppTheme.light; | ||
Get.changeThemeMode(isLightMode.value ? ThemeMode.light : ThemeMode.dark); | ||
final storedTheme = await _secureStorageProvider.readThemeValue(); | ||
dev.log(storedTheme.toString()); | ||
if (storedTheme == AppTheme.system) { | ||
_startBrightnessCheckTimer(); | ||
final systemBrightness = Get.mediaQuery.platformBrightness; | ||
isSystemModeActive = true; | ||
if (systemBrightness == Brightness.light) { | ||
isLightMode.value = true; | ||
Get.changeThemeMode(ThemeMode.light); | ||
onClose(); | ||
} else { | ||
isLightMode.value = false; | ||
Get.changeThemeMode(ThemeMode.dark); | ||
onClose(); | ||
} | ||
} else { | ||
onClose(); | ||
isLightMode.value = storedTheme == AppTheme.light; | ||
Get.changeThemeMode(isLightMode.value ? ThemeMode.light : ThemeMode.dark); | ||
} | ||
} | ||
|
||
void _saveThemeValuePreference() async { | ||
await _secureStorageProvider.writeThemeValue( | ||
theme: isLightMode.value ? AppTheme.light : AppTheme.dark, | ||
); | ||
void _startBrightnessCheckTimer() { | ||
_brightnessCheckTimer = Timer.periodic(Duration(seconds: 2), (timer) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This Timer is going to be resource intensive for something as simple as a theme change. We can just make it work on app startup and not keep it "real time" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But if so then it wouln't check for light or dark difference right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait, as I understand this timer is to check if the the device's theme is dark or light and it updates the theme on the app right? The timer runs every 2 seconds to check this, right? I think we can just set the app to the appropriate theme when it's opened up rather than having to change it in realtime. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct me if I am wrong, but isn't that how system theme works? As in if my app in System mode, it needs to check if my device is in light or dark mode right? It is always listening for changes, If we are checking just at the start of the app then its going to be simply like a default light and dark mode yea? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe, but this timer is gonna be unnecessary at the minute. I think we should just set the appropriate theme when the app is restarted, we don't really need it to be real-time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. Fine. I will make that change. |
||
_updateThemeFromSystemBrightness(); | ||
}); | ||
} | ||
|
||
void _updateThemeFromSystemBrightness() { | ||
if (isSystemModeActive) { | ||
final systemBrightness = Get.mediaQuery.platformBrightness; | ||
|
||
if (systemBrightness == Brightness.light) { | ||
isLightMode.value = true; | ||
Get.changeThemeMode(ThemeMode.light); | ||
} else { | ||
isLightMode.value = false; | ||
Get.changeThemeMode(ThemeMode.dark); | ||
} | ||
} | ||
} | ||
|
||
void _saveThemeValuePreference({required AppTheme theme}) async { | ||
print(']]]]]]]]]]]]]]]$isSystemModeActive ${isLightMode.value.toString()}'); | ||
await _secureStorageProvider.writeThemeValue(theme: theme); | ||
} | ||
|
||
void toggleThemeValue(bool enabled) { | ||
isSystemModeActive = false; | ||
isLightMode.value = enabled; | ||
_saveThemeValuePreference(); | ||
_saveThemeValuePreference( | ||
theme: isLightMode.value ? AppTheme.light : AppTheme.dark); | ||
onClose(); | ||
} | ||
|
||
void toggleSystemTheme() { | ||
isSystemModeActive = true; | ||
_startBrightnessCheckTimer(); | ||
final systemBrightness = Get.mediaQuery.platformBrightness; | ||
print("System Brightness: $systemBrightness"); | ||
|
||
Get.changeThemeMode(ThemeMode.system); | ||
_saveThemeValuePreference(theme: AppTheme.system); | ||
|
||
if (systemBrightness == Brightness.light) { | ||
isLightMode.value = true; | ||
Get.changeThemeMode(ThemeMode.light); | ||
} else { | ||
isLightMode.value = false; | ||
Get.changeThemeMode(ThemeMode.dark); | ||
} | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should rework this from Stateful Widget to work with GetX state mangement. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is still a Stateful Widget, it will make more sense to use GetX to keep it consistent. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,18 +5,19 @@ import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_cont | |
import 'package:ultimate_alarm_clock/app/utils/constants.dart'; | ||
import 'package:ultimate_alarm_clock/app/utils/utils.dart'; | ||
|
||
import '../../../data/providers/secure_storage_provider.dart'; | ||
|
||
class ThemeValueTile extends StatefulWidget { | ||
const ThemeValueTile({ | ||
super.key, | ||
Key? key, | ||
required this.controller, | ||
required this.height, | ||
required this.width, | ||
required this.themeController, | ||
}); | ||
}) : super(key: key); | ||
|
||
final SettingsController controller; | ||
final ThemeController themeController; | ||
|
||
final double height; | ||
final double width; | ||
|
||
|
@@ -25,43 +26,115 @@ class ThemeValueTile extends StatefulWidget { | |
} | ||
|
||
class _ThemeValueTileState extends State<ThemeValueTile> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this a Stateful widget? Could you please explain why this is declared as a stateful widget when GetX is being used for state management within this file? |
||
final _secureStorageProvider = SecureStorageProvider(); | ||
String appthem = ''; | ||
duckcommit marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@override | ||
void initState() { | ||
getAppTheme(); | ||
super.initState(); | ||
} | ||
|
||
void getAppTheme()async{ | ||
if(await _secureStorageProvider.readThemeValue() == AppTheme.system){ | ||
setState(() { | ||
appthem = 'System Mode'; | ||
}); | ||
} | ||
if(await _secureStorageProvider.readThemeValue() == AppTheme.light){ | ||
setState(() { | ||
appthem = 'Light Mode'; | ||
}); | ||
} | ||
if(await _secureStorageProvider.readThemeValue() == AppTheme.dark){ | ||
setState(() { | ||
appthem = 'Dark Mode'; | ||
}); | ||
} | ||
|
||
|
||
} | ||
@override | ||
Widget build(BuildContext context) { | ||
return Obx( | ||
() => Container( | ||
width: widget.width * 0.91, | ||
height: widget.height * 0.1, | ||
decoration: Utils.getCustomTileBoxDecoration( | ||
isLightMode: widget.themeController.isLightMode.value, | ||
), | ||
child: Padding( | ||
padding: EdgeInsets.only(left: 30, right: 20), | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
children: [ | ||
Expanded( | ||
child: Text( | ||
'Enable Light Mode'.tr, | ||
style: Theme.of(context).textTheme.bodyLarge, | ||
return Container( | ||
width: widget.width * 0.91, | ||
height: widget.height * 0.1, | ||
decoration: Utils.getCustomTileBoxDecoration( | ||
isLightMode: widget.themeController.isLightMode.value, | ||
), | ||
child: Padding( | ||
padding: EdgeInsets.only(left: 30), | ||
child: Row( | ||
children: [ | ||
DropdownMenu( | ||
menuStyle: MenuStyle( | ||
backgroundColor: MaterialStatePropertyAll( | ||
widget.themeController.isLightMode.value | ||
? kLightSecondaryBackgroundColor | ||
: ksecondaryBackgroundColor, | ||
), | ||
), | ||
Obx( | ||
() => Switch.adaptive( | ||
value: widget.themeController.isLightMode.value, | ||
activeColor: ksecondaryColor, | ||
onChanged: (bool value) async { | ||
widget.themeController.toggleThemeValue(value); | ||
Get.changeThemeMode( | ||
inputDecorationTheme: | ||
InputDecorationTheme(enabledBorder: InputBorder.none), | ||
trailingIcon: Icon( | ||
Icons.arrow_drop_down_outlined, | ||
size: 40.0, | ||
color: widget.themeController.isLightMode.value | ||
? kLightPrimaryTextColor.withOpacity(0.8) | ||
: kprimaryTextColor.withOpacity(0.8), | ||
), | ||
width: widget.width * 0.78, | ||
initialSelection: appthem, | ||
label: Text('Select Theme'), | ||
dropdownMenuEntries: [ | ||
DropdownMenuEntry( | ||
value: 'System Mode', | ||
label: 'System Mode', | ||
style: ButtonStyle( | ||
foregroundColor: MaterialStatePropertyAll( | ||
widget.themeController.isLightMode.value | ||
? ThemeMode.light | ||
: ThemeMode.dark, | ||
); | ||
Utils.hapticFeedback(); | ||
}, | ||
? kLightPrimaryTextColor | ||
: kprimaryTextColor, | ||
), | ||
), | ||
), | ||
), | ||
], | ||
), | ||
DropdownMenuEntry( | ||
value: 'Light Mode', | ||
label: 'Light Mode', | ||
style: ButtonStyle( | ||
foregroundColor: MaterialStatePropertyAll( | ||
widget.themeController.isLightMode.value | ||
? kLightPrimaryTextColor | ||
: kprimaryTextColor, | ||
), | ||
), | ||
), | ||
DropdownMenuEntry( | ||
value: 'Dark Mode', | ||
label: 'Dark Mode', | ||
style: ButtonStyle( | ||
foregroundColor: MaterialStatePropertyAll( | ||
widget.themeController.isLightMode.value | ||
? kLightPrimaryTextColor | ||
: kprimaryTextColor, | ||
), | ||
), | ||
), | ||
], | ||
onSelected: (newValue) { | ||
if (newValue == 'System Mode') { | ||
widget.themeController.toggleSystemTheme(); | ||
} else if (newValue == 'Light Mode') { | ||
widget.themeController.toggleThemeValue(true); | ||
Get.changeThemeMode(ThemeMode.light); | ||
} else { | ||
widget.themeController.toggleThemeValue(false); | ||
Get.changeThemeMode(ThemeMode.dark); | ||
} | ||
Utils.hapticFeedback(); | ||
|
||
}, | ||
), | ||
], | ||
), | ||
), | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just check this from the theme details that we're alreading storing, this variable would not be needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that, but the problem was that, either the system mode text won't update, or else it won't change completely to light or dark issue. Weird issue but this was the most optimized way I found out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate more on this? We are storing the theme details in storage and fetching it in
onInit
so we should have access to the option chosen by the user, yes?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this existing method, we can retrieve the current theme settings. But it isn't working properly for the system mode. As in, all the elements/widgets are not changing accordingly. So I had to do it manually itself. For that I created a new variable. Again it is a weird issue, but this was the only work around for it. Its the optimal one for now.