diff --git a/lib/app/modules/cards/widgets/create_card_weather.dart b/lib/app/modules/cards/widgets/create_card_weather.dart index 8983091..6e3254a 100644 --- a/lib/app/modules/cards/widgets/create_card_weather.dart +++ b/lib/app/modules/cards/widgets/create_card_weather.dart @@ -45,205 +45,208 @@ class _CreateWeatherCardState extends State { @override Widget build(BuildContext context) { const kTextFieldElevation = 4.0; - return Form( - key: formKey, - child: SingleChildScrollView( - child: Stack( - children: [ - Padding( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).viewInsets.bottom, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 10, 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - IconButton( - onPressed: () { - Get.back(); - }, - icon: const Icon( - Iconsax.close_square, - size: 18, + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: Form( + key: formKey, + child: SingleChildScrollView( + child: Stack( + children: [ + Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 10, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + onPressed: () { + Get.back(); + }, + icon: const Icon( + Iconsax.close_square, + size: 18, + ), ), - ), - Text( - 'create'.tr, - style: context.textTheme.titleLarge?.copyWith( - fontSize: 20, + Text( + 'create'.tr, + style: context.textTheme.titleLarge?.copyWith( + fontSize: 20, + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, - ), - IconButton( - onPressed: () async { - if (formKey.currentState!.validate()) { - textTrim(_controllerLat); - textTrim(_controllerLon); - textTrim(_controllerCity); - textTrim(_controllerDistrict); - setState(() => isLoading = true); - await weatherController.addCardWeather( - double.parse(_controllerLat.text), - double.parse(_controllerLon.text), - _controllerCity.text, - _controllerDistrict.text, - ); - setState(() => isLoading = false); - Get.back(); - } - }, - icon: const Icon( - Iconsax.tick_square, - size: 18, + IconButton( + onPressed: () async { + if (formKey.currentState!.validate()) { + textTrim(_controllerLat); + textTrim(_controllerLon); + textTrim(_controllerCity); + textTrim(_controllerDistrict); + setState(() => isLoading = true); + await weatherController.addCardWeather( + double.parse(_controllerLat.text), + double.parse(_controllerLon.text), + _controllerCity.text, + _controllerDistrict.text, + ); + setState(() => isLoading = false); + Get.back(); + } + }, + icon: const Icon( + Iconsax.tick_square, + size: 18, + ), ), - ), - ], + ], + ), ), - ), - RawAutocomplete( - focusNode: _focusNode, - textEditingController: _controller, - fieldViewBuilder: (BuildContext context, - TextEditingController fieldTextEditingController, - FocusNode fieldFocusNode, - VoidCallback onFieldSubmitted) { - return MyTextForm( - elevation: kTextFieldElevation, - labelText: 'search'.tr, - type: TextInputType.text, - icon: const Icon(Iconsax.global_search), - controller: _controller, - margin: - const EdgeInsets.only(left: 10, right: 10, top: 10), - focusNode: _focusNode, - ); - }, - optionsBuilder: (TextEditingValue textEditingValue) { - if (textEditingValue.text.isEmpty) { - return const Iterable.empty(); - } - return WeatherAPI() - .getCity(textEditingValue.text, locale); - }, - onSelected: (Result selection) => fillController(selection), - displayStringForOption: (Result option) => - '${option.name}, ${option.admin1}', - optionsViewBuilder: (BuildContext context, - AutocompleteOnSelected onSelected, - Iterable options) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Align( - alignment: Alignment.topCenter, - child: Material( - borderRadius: BorderRadius.circular(20), - elevation: 4.0, - child: ListView.builder( - padding: EdgeInsets.zero, - shrinkWrap: true, - itemCount: options.length, - itemBuilder: (BuildContext context, int index) { - final Result option = options.elementAt(index); - return InkWell( - onTap: () => onSelected(option), - child: ListTile( - title: Text( - '${option.name}, ${option.admin1}', - style: context.textTheme.labelLarge, + RawAutocomplete( + focusNode: _focusNode, + textEditingController: _controller, + fieldViewBuilder: (BuildContext context, + TextEditingController fieldTextEditingController, + FocusNode fieldFocusNode, + VoidCallback onFieldSubmitted) { + return MyTextForm( + elevation: kTextFieldElevation, + labelText: 'search'.tr, + type: TextInputType.text, + icon: const Icon(Iconsax.global_search), + controller: _controller, + margin: + const EdgeInsets.only(left: 10, right: 10, top: 10), + focusNode: _focusNode, + ); + }, + optionsBuilder: (TextEditingValue textEditingValue) { + if (textEditingValue.text.isEmpty) { + return const Iterable.empty(); + } + return WeatherAPI() + .getCity(textEditingValue.text, locale); + }, + onSelected: (Result selection) => fillController(selection), + displayStringForOption: (Result option) => + '${option.name}, ${option.admin1}', + optionsViewBuilder: (BuildContext context, + AutocompleteOnSelected onSelected, + Iterable options) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Align( + alignment: Alignment.topCenter, + child: Material( + borderRadius: BorderRadius.circular(20), + elevation: 4.0, + child: ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: options.length, + itemBuilder: (BuildContext context, int index) { + final Result option = options.elementAt(index); + return InkWell( + onTap: () => onSelected(option), + child: ListTile( + title: Text( + '${option.name}, ${option.admin1}', + style: context.textTheme.labelLarge, + ), ), - ), - ); - }, + ); + }, + ), ), ), - ), - ); - }, - ), - MyTextForm( - elevation: kTextFieldElevation, - controller: _controllerLat, - labelText: 'lat'.tr, - type: TextInputType.number, - icon: const Icon(Iconsax.location), - margin: const EdgeInsets.only(left: 10, right: 10, top: 10), - validator: (value) { - if (value == null || value.isEmpty) { - return 'validateValue'.tr; - } - double? numericValue = double.tryParse(value); - if (numericValue == null) { - return 'validateNumber'.tr; - } - if (numericValue < -90 || numericValue > 90) { - return 'validate90'.tr; - } - return null; - }, - ), - MyTextForm( - elevation: kTextFieldElevation, - controller: _controllerLon, - labelText: 'lon'.tr, - type: TextInputType.number, - icon: const Icon(Iconsax.location), - margin: const EdgeInsets.only(left: 10, right: 10, top: 10), - validator: (value) { - if (value == null || value.isEmpty) { - return 'validateValue'.tr; - } - double? numericValue = double.tryParse(value); - if (numericValue == null) { - return 'validateNumber'.tr; - } - if (numericValue < -180 || numericValue > 180) { - return 'validate180'.tr; - } - return null; - }, - ), - MyTextForm( - elevation: kTextFieldElevation, - controller: _controllerCity, - labelText: 'city'.tr, - type: TextInputType.name, - icon: const Icon(Icons.location_city_rounded), - margin: const EdgeInsets.only(left: 10, right: 10, top: 10), - validator: (value) { - if (value == null || value.isEmpty) { - return 'validateName'.tr; - } - return null; - }, - ), - MyTextForm( - elevation: kTextFieldElevation, - controller: _controllerDistrict, - labelText: 'district'.tr, - type: TextInputType.streetAddress, - icon: const Icon(Iconsax.global), - margin: const EdgeInsets.only(left: 10, right: 10, top: 10), - validator: (value) { - if (value == null || value.isEmpty) { - return 'validateName'.tr; - } - return null; - }, - ), - const SizedBox(height: 20), - ], - ), - ), - if (isLoading) - const Center( - child: CircularProgressIndicator(), + ); + }, + ), + MyTextForm( + elevation: kTextFieldElevation, + controller: _controllerLat, + labelText: 'lat'.tr, + type: TextInputType.number, + icon: const Icon(Iconsax.location), + margin: const EdgeInsets.only(left: 10, right: 10, top: 10), + validator: (value) { + if (value == null || value.isEmpty) { + return 'validateValue'.tr; + } + double? numericValue = double.tryParse(value); + if (numericValue == null) { + return 'validateNumber'.tr; + } + if (numericValue < -90 || numericValue > 90) { + return 'validate90'.tr; + } + return null; + }, + ), + MyTextForm( + elevation: kTextFieldElevation, + controller: _controllerLon, + labelText: 'lon'.tr, + type: TextInputType.number, + icon: const Icon(Iconsax.location), + margin: const EdgeInsets.only(left: 10, right: 10, top: 10), + validator: (value) { + if (value == null || value.isEmpty) { + return 'validateValue'.tr; + } + double? numericValue = double.tryParse(value); + if (numericValue == null) { + return 'validateNumber'.tr; + } + if (numericValue < -180 || numericValue > 180) { + return 'validate180'.tr; + } + return null; + }, + ), + MyTextForm( + elevation: kTextFieldElevation, + controller: _controllerCity, + labelText: 'city'.tr, + type: TextInputType.name, + icon: const Icon(Icons.location_city_rounded), + margin: const EdgeInsets.only(left: 10, right: 10, top: 10), + validator: (value) { + if (value == null || value.isEmpty) { + return 'validateName'.tr; + } + return null; + }, + ), + MyTextForm( + elevation: kTextFieldElevation, + controller: _controllerDistrict, + labelText: 'district'.tr, + type: TextInputType.streetAddress, + icon: const Icon(Iconsax.global), + margin: const EdgeInsets.only(left: 10, right: 10, top: 10), + validator: (value) { + if (value == null || value.isEmpty) { + return 'validateName'.tr; + } + return null; + }, + ), + const SizedBox(height: 20), + ], + ), ), - ], + if (isLoading) + const Center( + child: CircularProgressIndicator(), + ), + ], + ), ), ), ); diff --git a/lib/app/modules/settings/view/settings.dart b/lib/app/modules/settings/view/settings.dart index 9f03999..d97259c 100644 --- a/lib/app/modules/settings/view/settings.dart +++ b/lib/app/modules/settings/view/settings.dart @@ -69,82 +69,85 @@ class _SettingsPageState extends State { showModalBottomSheet( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, setState) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 15), - child: Text( - 'appearance'.tr, - style: context.textTheme.titleLarge?.copyWith( - fontSize: 20, + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: StatefulBuilder( + builder: (BuildContext context, setState) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Text( + 'appearance'.tr, + style: context.textTheme.titleLarge?.copyWith( + fontSize: 20, + ), ), ), - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.moon), - text: 'theme'.tr, - dropdown: true, - dropdownName: settings.theme?.tr, - dropdownList: [ - 'system'.tr, - 'dark'.tr, - 'light'.tr - ], - dropdownCange: (String? newValue) { - final newThemeMode = newValue?.tr; - final darkTheme = 'dark'.tr; - final systemTheme = 'system'.tr; - ThemeMode themeMode = - newThemeMode == systemTheme - ? ThemeMode.system - : newThemeMode == darkTheme - ? ThemeMode.dark - : ThemeMode.light; - String theme = newThemeMode == systemTheme - ? 'system' - : newThemeMode == darkTheme - ? 'dark' - : 'light'; - themeController.saveTheme(theme); - themeController.changeThemeMode(themeMode); - setState(() {}); - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.mobile), - text: 'amoledTheme'.tr, - switcher: true, - value: settings.amoledTheme, - onChange: (value) { - themeController.saveOledTheme(value); - MyApp.updateAppState(context, - newAmoledTheme: value); - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.colorfilter), - text: 'materialColor'.tr, - switcher: true, - value: settings.materialColor, - onChange: (value) { - themeController.saveMaterialTheme(value); - MyApp.updateAppState(context, - newMaterialColor: value); - }, - ), - const SizedBox(height: 10), - ], - ), - ); - }, + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.moon), + text: 'theme'.tr, + dropdown: true, + dropdownName: settings.theme?.tr, + dropdownList: [ + 'system'.tr, + 'dark'.tr, + 'light'.tr + ], + dropdownCange: (String? newValue) { + final newThemeMode = newValue?.tr; + final darkTheme = 'dark'.tr; + final systemTheme = 'system'.tr; + ThemeMode themeMode = + newThemeMode == systemTheme + ? ThemeMode.system + : newThemeMode == darkTheme + ? ThemeMode.dark + : ThemeMode.light; + String theme = newThemeMode == systemTheme + ? 'system' + : newThemeMode == darkTheme + ? 'dark' + : 'light'; + themeController.saveTheme(theme); + themeController.changeThemeMode(themeMode); + setState(() {}); + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.mobile), + text: 'amoledTheme'.tr, + switcher: true, + value: settings.amoledTheme, + onChange: (value) { + themeController.saveOledTheme(value); + MyApp.updateAppState(context, + newAmoledTheme: value); + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.colorfilter), + text: 'materialColor'.tr, + switcher: true, + value: settings.materialColor, + onChange: (value) { + themeController.saveMaterialTheme(value); + MyApp.updateAppState(context, + newMaterialColor: value); + }, + ), + const SizedBox(height: 10), + ], + ), + ); + }, + ), ); }, ); @@ -157,268 +160,271 @@ class _SettingsPageState extends State { showModalBottomSheet( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, setState) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 15), - child: Text( - 'functions'.tr, - style: context.textTheme.titleLarge?.copyWith( - fontSize: 20, + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: StatefulBuilder( + builder: (BuildContext context, setState) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Text( + 'functions'.tr, + style: context.textTheme.titleLarge?.copyWith( + fontSize: 20, + ), ), ), - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.map_1), - text: 'location'.tr, - switcher: true, - value: settings.location, - onChange: (value) async { - if (value) { - bool serviceEnabled = await Geolocator - .isLocationServiceEnabled(); - if (!serviceEnabled) { - if (!context.mounted) return; - await showAdaptiveDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog.adaptive( - title: Text( - 'location'.tr, - style: context.textTheme.titleLarge, - ), - content: Text('no_location'.tr, - style: context - .textTheme.titleMedium), - actions: [ - TextButton( - onPressed: () => - Get.back(result: false), - child: Text( - 'cancel'.tr, - style: context - .textTheme.titleMedium - ?.copyWith( - color: - Colors.blueAccent), - ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.map_1), + text: 'location'.tr, + switcher: true, + value: settings.location, + onChange: (value) async { + if (value) { + bool serviceEnabled = await Geolocator + .isLocationServiceEnabled(); + if (!serviceEnabled) { + if (!context.mounted) return; + await showAdaptiveDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog.adaptive( + title: Text( + 'location'.tr, + style: context.textTheme.titleLarge, ), - TextButton( - onPressed: () { - Geolocator - .openLocationSettings(); - Get.back(result: true); - }, - child: Text( - 'settings'.tr, + content: Text('no_location'.tr, style: context - .textTheme.titleMedium - ?.copyWith( - color: Colors.green), + .textTheme.titleMedium), + actions: [ + TextButton( + onPressed: () => + Get.back(result: false), + child: Text( + 'cancel'.tr, + style: context + .textTheme.titleMedium + ?.copyWith( + color: + Colors.blueAccent), + ), ), - ), - ], - ); - }, - ); - - return; + TextButton( + onPressed: () { + Geolocator + .openLocationSettings(); + Get.back(result: true); + }, + child: Text( + 'settings'.tr, + style: context + .textTheme.titleMedium + ?.copyWith( + color: Colors.green), + ), + ), + ], + ); + }, + ); + + return; + } + weatherController.getCurrentLocation(); } - weatherController.getCurrentLocation(); - } - isar.writeTxnSync(() { - settings.location = value; - isar.settings.putSync(settings); - }); - setState(() {}); - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.notification_1), - text: 'notifications'.tr, - switcher: true, - value: settings.notifications, - onChange: (value) async { - final resultExact = - await flutterLocalNotificationsPlugin - .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.requestExactAlarmsPermission(); - final result = Platform.isIOS - ? await flutterLocalNotificationsPlugin - .resolvePlatformSpecificImplementation< - IOSFlutterLocalNotificationsPlugin>() - ?.requestPermissions() - : await flutterLocalNotificationsPlugin - .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.requestNotificationsPermission(); - if (result != null && resultExact != null) { isar.writeTxnSync(() { - settings.notifications = value; + settings.location = value; isar.settings.putSync(settings); }); - if (value) { - weatherController.notification( - weatherController.mainWeather); - } else { - flutterLocalNotificationsPlugin.cancelAll(); - } setState(() {}); - } - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.notification_status), - text: 'timeRange'.tr, - dropdown: true, - dropdownName: '$timeRange', - dropdownList: const [ - '1', - '2', - '3', - '4', - '5', - ], - dropdownCange: (String? newValue) { - isar.writeTxnSync(() { - settings.timeRange = int.parse(newValue!); - isar.settings.putSync(settings); - }); - MyApp.updateAppState(context, - newTimeRange: int.parse(newValue!)); - if (settings.notifications) { - flutterLocalNotificationsPlugin.cancelAll(); - weatherController.notification( - weatherController.mainWeather); - } - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.timer_start), - text: 'timeStart'.tr, - info: true, - infoSettings: true, - infoWidget: _TextInfo( - info: settings.timeformat == '12' - ? DateFormat.jm(locale.languageCode).format( - DateFormat.Hm(locale.languageCode) - .parse(weatherController - .timeConvert(timeStart) - .format(context))) - : DateFormat.Hm(locale.languageCode).format( - DateFormat.Hm(locale.languageCode) - .parse(weatherController - .timeConvert(timeStart) - .format(context))), + }, ), - onPressed: () async { - final TimeOfDay? timeStartPicker = - await showTimePicker( - context: context, - initialTime: - weatherController.timeConvert(timeStart), - builder: (context, child) { - final Widget mediaQueryWrapper = MediaQuery( - data: MediaQuery.of(context).copyWith( - alwaysUse24HourFormat: - settings.timeformat == '12' - ? false - : true, - ), - child: child!, - ); - return mediaQueryWrapper; - }, - ); - if (timeStartPicker != null) { - isar.writeTxnSync(() { - settings.timeStart = - timeStartPicker.format(context); - isar.settings.putSync(settings); - }); - if (!context.mounted) return; - MyApp.updateAppState(context, - newTimeStart: - timeStartPicker.format(context)); - if (settings.notifications) { - flutterLocalNotificationsPlugin.cancelAll(); - weatherController.notification( - weatherController.mainWeather); + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.notification_1), + text: 'notifications'.tr, + switcher: true, + value: settings.notifications, + onChange: (value) async { + final resultExact = + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.requestExactAlarmsPermission(); + final result = Platform.isIOS + ? await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + IOSFlutterLocalNotificationsPlugin>() + ?.requestPermissions() + : await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.requestNotificationsPermission(); + if (result != null && resultExact != null) { + isar.writeTxnSync(() { + settings.notifications = value; + isar.settings.putSync(settings); + }); + if (value) { + weatherController.notification( + weatherController.mainWeather); + } else { + flutterLocalNotificationsPlugin.cancelAll(); + } + setState(() {}); } - } - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.timer_pause), - text: 'timeEnd'.tr, - info: true, - infoSettings: true, - infoWidget: _TextInfo( - info: settings.timeformat == '12' - ? DateFormat.jm(locale.languageCode).format( - DateFormat.Hm(locale.languageCode) - .parse(weatherController - .timeConvert(timeEnd) - .format(context))) - : DateFormat.Hm(locale.languageCode).format( - DateFormat.Hm(locale.languageCode) - .parse(weatherController - .timeConvert(timeEnd) - .format(context))), + }, ), - onPressed: () async { - final TimeOfDay? timeEndPicker = - await showTimePicker( - context: context, - initialTime: - weatherController.timeConvert(timeEnd), - builder: (context, child) { - final Widget mediaQueryWrapper = MediaQuery( - data: MediaQuery.of(context).copyWith( - alwaysUse24HourFormat: - settings.timeformat == '12' - ? false - : true, - ), - child: child!, - ); - return mediaQueryWrapper; - }, - ); - if (timeEndPicker != null) { + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.notification_status), + text: 'timeRange'.tr, + dropdown: true, + dropdownName: '$timeRange', + dropdownList: const [ + '1', + '2', + '3', + '4', + '5', + ], + dropdownCange: (String? newValue) { isar.writeTxnSync(() { - settings.timeEnd = - timeEndPicker.format(context); + settings.timeRange = int.parse(newValue!); isar.settings.putSync(settings); }); - if (!context.mounted) return; MyApp.updateAppState(context, - newTimeEnd: - timeEndPicker.format(context)); + newTimeRange: int.parse(newValue!)); if (settings.notifications) { flutterLocalNotificationsPlugin.cancelAll(); weatherController.notification( weatherController.mainWeather); } - } - }, - ), - const SizedBox(height: 10), - ], - ), - ); - }, + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.timer_start), + text: 'timeStart'.tr, + info: true, + infoSettings: true, + infoWidget: _TextInfo( + info: settings.timeformat == '12' + ? DateFormat.jm(locale.languageCode).format( + DateFormat.Hm(locale.languageCode) + .parse(weatherController + .timeConvert(timeStart) + .format(context))) + : DateFormat.Hm(locale.languageCode).format( + DateFormat.Hm(locale.languageCode) + .parse(weatherController + .timeConvert(timeStart) + .format(context))), + ), + onPressed: () async { + final TimeOfDay? timeStartPicker = + await showTimePicker( + context: context, + initialTime: + weatherController.timeConvert(timeStart), + builder: (context, child) { + final Widget mediaQueryWrapper = MediaQuery( + data: MediaQuery.of(context).copyWith( + alwaysUse24HourFormat: + settings.timeformat == '12' + ? false + : true, + ), + child: child!, + ); + return mediaQueryWrapper; + }, + ); + if (timeStartPicker != null) { + isar.writeTxnSync(() { + settings.timeStart = + timeStartPicker.format(context); + isar.settings.putSync(settings); + }); + if (!context.mounted) return; + MyApp.updateAppState(context, + newTimeStart: + timeStartPicker.format(context)); + if (settings.notifications) { + flutterLocalNotificationsPlugin.cancelAll(); + weatherController.notification( + weatherController.mainWeather); + } + } + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.timer_pause), + text: 'timeEnd'.tr, + info: true, + infoSettings: true, + infoWidget: _TextInfo( + info: settings.timeformat == '12' + ? DateFormat.jm(locale.languageCode).format( + DateFormat.Hm(locale.languageCode) + .parse(weatherController + .timeConvert(timeEnd) + .format(context))) + : DateFormat.Hm(locale.languageCode).format( + DateFormat.Hm(locale.languageCode) + .parse(weatherController + .timeConvert(timeEnd) + .format(context))), + ), + onPressed: () async { + final TimeOfDay? timeEndPicker = + await showTimePicker( + context: context, + initialTime: + weatherController.timeConvert(timeEnd), + builder: (context, child) { + final Widget mediaQueryWrapper = MediaQuery( + data: MediaQuery.of(context).copyWith( + alwaysUse24HourFormat: + settings.timeformat == '12' + ? false + : true, + ), + child: child!, + ); + return mediaQueryWrapper; + }, + ); + if (timeEndPicker != null) { + isar.writeTxnSync(() { + settings.timeEnd = + timeEndPicker.format(context); + isar.settings.putSync(settings); + }); + if (!context.mounted) return; + MyApp.updateAppState(context, + newTimeEnd: + timeEndPicker.format(context)); + if (settings.notifications) { + flutterLocalNotificationsPlugin.cancelAll(); + weatherController.notification( + weatherController.mainWeather); + } + } + }, + ), + const SizedBox(height: 10), + ], + ), + ); + }, + ), ); }, ); @@ -431,108 +437,111 @@ class _SettingsPageState extends State { showModalBottomSheet( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, setState) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 15), - child: Text( - 'data'.tr, - style: context.textTheme.titleLarge?.copyWith( - fontSize: 20, + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: StatefulBuilder( + builder: (BuildContext context, setState) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Text( + 'data'.tr, + style: context.textTheme.titleLarge?.copyWith( + fontSize: 20, + ), ), ), - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.cloud_notif), - text: 'roundDegree'.tr, - switcher: true, - value: settings.roundDegree, - onChange: (value) { - settings.roundDegree = value; - isar.writeTxnSync( - () => isar.settings.putSync(settings), - ); - MyApp.updateAppState( - context, - newRoundDegree: value, - ); - setState(() {}); - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.sun_1), - text: 'degrees'.tr, - dropdown: true, - dropdownName: settings.degrees.tr, - dropdownList: [ - 'celsius'.tr, - 'fahrenheit'.tr - ], - dropdownCange: (String? newValue) async { - isar.writeTxnSync(() { - settings.degrees = newValue == 'celsius'.tr - ? 'celsius' - : 'fahrenheit'; - isar.settings.putSync(settings); - }); - await weatherController.deleteAll(false); - await weatherController.setLocation(); - await weatherController.updateCacheCard(true); - setState(() {}); - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.rulerpen), - text: 'measurements'.tr, - dropdown: true, - dropdownName: settings.measurements.tr, - dropdownList: [ - 'metric'.tr, - 'imperial'.tr - ], - dropdownCange: (String? newValue) async { - isar.writeTxnSync(() { - settings.measurements = - newValue == 'metric'.tr - ? 'metric' - : 'imperial'; - isar.settings.putSync(settings); - }); - await weatherController.deleteAll(false); - await weatherController.setLocation(); - await weatherController.updateCacheCard(true); - setState(() {}); - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.clock), - text: 'timeformat'.tr, - dropdown: true, - dropdownName: settings.timeformat.tr, - dropdownList: ['12'.tr, '24'.tr], - dropdownCange: (String? newValue) { - isar.writeTxnSync(() { - settings.timeformat = - newValue == '12'.tr ? '12' : '24'; - isar.settings.putSync(settings); - }); - setState(() {}); - }, - ), - const SizedBox(height: 10), - ], - ), - ); - }, + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.cloud_notif), + text: 'roundDegree'.tr, + switcher: true, + value: settings.roundDegree, + onChange: (value) { + settings.roundDegree = value; + isar.writeTxnSync( + () => isar.settings.putSync(settings), + ); + MyApp.updateAppState( + context, + newRoundDegree: value, + ); + setState(() {}); + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.sun_1), + text: 'degrees'.tr, + dropdown: true, + dropdownName: settings.degrees.tr, + dropdownList: [ + 'celsius'.tr, + 'fahrenheit'.tr + ], + dropdownCange: (String? newValue) async { + isar.writeTxnSync(() { + settings.degrees = newValue == 'celsius'.tr + ? 'celsius' + : 'fahrenheit'; + isar.settings.putSync(settings); + }); + await weatherController.deleteAll(false); + await weatherController.setLocation(); + await weatherController.updateCacheCard(true); + setState(() {}); + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.rulerpen), + text: 'measurements'.tr, + dropdown: true, + dropdownName: settings.measurements.tr, + dropdownList: [ + 'metric'.tr, + 'imperial'.tr + ], + dropdownCange: (String? newValue) async { + isar.writeTxnSync(() { + settings.measurements = + newValue == 'metric'.tr + ? 'metric' + : 'imperial'; + isar.settings.putSync(settings); + }); + await weatherController.deleteAll(false); + await weatherController.setLocation(); + await weatherController.updateCacheCard(true); + setState(() {}); + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.clock), + text: 'timeformat'.tr, + dropdown: true, + dropdownName: settings.timeformat.tr, + dropdownList: ['12'.tr, '24'.tr], + dropdownCange: (String? newValue) { + isar.writeTxnSync(() { + settings.timeformat = + newValue == '12'.tr ? '12' : '24'; + isar.settings.putSync(settings); + }); + setState(() {}); + }, + ), + const SizedBox(height: 10), + ], + ), + ); + }, + ), ); }, ); @@ -545,198 +554,201 @@ class _SettingsPageState extends State { showModalBottomSheet( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, setState) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 15), - child: Text( - 'widget'.tr, - style: context.textTheme.titleLarge?.copyWith( - fontSize: 20, + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: StatefulBuilder( + builder: (BuildContext context, setState) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Text( + 'widget'.tr, + style: context.textTheme.titleLarge?.copyWith( + fontSize: 20, + ), ), ), - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.bucket_square), - text: 'widgetBackground'.tr, - info: true, - infoWidget: CircleAvatar( - backgroundColor: context.theme.indicatorColor, - radius: 11, - child: CircleAvatar( - backgroundColor: widgetBackgroundColor.isEmpty - ? context.theme.primaryColor - : HexColor.fromHex(widgetBackgroundColor), - radius: 10, + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.bucket_square), + text: 'widgetBackground'.tr, + info: true, + infoWidget: CircleAvatar( + backgroundColor: context.theme.indicatorColor, + radius: 11, + child: CircleAvatar( + backgroundColor: widgetBackgroundColor.isEmpty + ? context.theme.primaryColor + : HexColor.fromHex(widgetBackgroundColor), + radius: 10, + ), ), - ), - onPressed: () { - colorBackground = null; - showDialog( - context: context, - builder: (context) => Dialog( - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 15), - child: Text( - 'widgetBackground'.tr, - style: context - .textTheme.titleMedium - ?.copyWith(fontSize: 18), + onPressed: () { + colorBackground = null; + showDialog( + context: context, + builder: (context) => Dialog( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 15), + child: Text( + 'widgetBackground'.tr, + style: context + .textTheme.titleMedium + ?.copyWith(fontSize: 18), + ), ), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 15), - child: Theme( - data: context.theme.copyWith( - inputDecorationTheme: - InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: - BorderRadius.circular( - 8), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15), + child: Theme( + data: context.theme.copyWith( + inputDecorationTheme: + InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular( + 8), + ), ), ), - ), - child: ColorPicker( - color: widgetBackgroundColor - .isEmpty - ? context.theme.primaryColor - : HexColor.fromHex( - widgetBackgroundColor), - onChanged: (pickedColor) { - colorBackground = - pickedColor.toHex(); - }, + child: ColorPicker( + color: widgetBackgroundColor + .isEmpty + ? context.theme.primaryColor + : HexColor.fromHex( + widgetBackgroundColor), + onChanged: (pickedColor) { + colorBackground = + pickedColor.toHex(); + }, + ), ), ), - ), - IconButton( - icon: const Icon( - Iconsax.tick_square, + IconButton( + icon: const Icon( + Iconsax.tick_square, + ), + onPressed: () { + if (colorBackground == null) { + return; + } + weatherController + .updateWidgetBackgroundColor( + colorBackground!); + MyApp.updateAppState(context, + newWidgetBackgroundColor: + colorBackground); + Get.back(); + }, ), - onPressed: () { - if (colorBackground == null) { - return; - } - weatherController - .updateWidgetBackgroundColor( - colorBackground!); - MyApp.updateAppState(context, - newWidgetBackgroundColor: - colorBackground); - Get.back(); - }, - ), - ], + ], + ), ), ), + ); + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.text_block), + text: 'widgetText'.tr, + info: true, + infoWidget: CircleAvatar( + backgroundColor: context.theme.indicatorColor, + radius: 11, + child: CircleAvatar( + backgroundColor: widgetTextColor.isEmpty + ? context.theme.primaryColor + : HexColor.fromHex(widgetTextColor), + radius: 10, ), - ); - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.text_block), - text: 'widgetText'.tr, - info: true, - infoWidget: CircleAvatar( - backgroundColor: context.theme.indicatorColor, - radius: 11, - child: CircleAvatar( - backgroundColor: widgetTextColor.isEmpty - ? context.theme.primaryColor - : HexColor.fromHex(widgetTextColor), - radius: 10, ), - ), - onPressed: () { - colorText = null; - showDialog( - context: context, - builder: (context) => Dialog( - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 15), - child: Text( - 'widgetText'.tr, - style: context - .textTheme.titleMedium - ?.copyWith(fontSize: 18), + onPressed: () { + colorText = null; + showDialog( + context: context, + builder: (context) => Dialog( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 15), + child: Text( + 'widgetText'.tr, + style: context + .textTheme.titleMedium + ?.copyWith(fontSize: 18), + ), ), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 15), - child: Theme( - data: context.theme.copyWith( - inputDecorationTheme: - InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: - BorderRadius.circular( - 8), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15), + child: Theme( + data: context.theme.copyWith( + inputDecorationTheme: + InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: + BorderRadius.circular( + 8), + ), ), ), - ), - child: ColorPicker( - color: widgetTextColor.isEmpty - ? context.theme.primaryColor - : HexColor.fromHex( - widgetTextColor), - onChanged: (pickedColor) { - colorText = - pickedColor.toHex(); - }, + child: ColorPicker( + color: widgetTextColor.isEmpty + ? context.theme.primaryColor + : HexColor.fromHex( + widgetTextColor), + onChanged: (pickedColor) { + colorText = + pickedColor.toHex(); + }, + ), ), ), - ), - IconButton( - icon: const Icon( - Iconsax.tick_square, + IconButton( + icon: const Icon( + Iconsax.tick_square, + ), + onPressed: () { + if (colorText == null) return; + weatherController + .updateWidgetTextColor( + colorText!); + MyApp.updateAppState(context, + newWidgetTextColor: + colorText); + Get.back(); + }, ), - onPressed: () { - if (colorText == null) return; - weatherController - .updateWidgetTextColor( - colorText!); - MyApp.updateAppState(context, - newWidgetTextColor: - colorText); - Get.back(); - }, - ), - ], + ], + ), ), ), - ), - ); - }, - ), - const SizedBox(height: 10), - ], - ), - ); - }, + ); + }, + ), + const SizedBox(height: 10), + ], + ), + ); + }, + ), ); }, ); @@ -756,50 +768,53 @@ class _SettingsPageState extends State { showModalBottomSheet( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, setState) { - return ListView( - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 15), - child: Text( - 'language'.tr, - style: context.textTheme.titleLarge?.copyWith( - fontSize: 20, + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: StatefulBuilder( + builder: (BuildContext context, setState) { + return ListView( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Text( + 'language'.tr, + style: context.textTheme.titleLarge?.copyWith( + fontSize: 20, + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, ), - ), - ListView.builder( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - itemCount: appLanguages.length, - itemBuilder: (context, index) { - return Card( - elevation: 4, - margin: const EdgeInsets.symmetric( - horizontal: 10, vertical: 5), - child: ListTile( - title: Text( - appLanguages[index]['name'], - style: context.textTheme.labelLarge, - textAlign: TextAlign.center, + ListView.builder( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemCount: appLanguages.length, + itemBuilder: (context, index) { + return Card( + elevation: 4, + margin: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + child: ListTile( + title: Text( + appLanguages[index]['name'], + style: context.textTheme.labelLarge, + textAlign: TextAlign.center, + ), + onTap: () { + MyApp.updateAppState(context, + newLocale: appLanguages[index] + ['locale']); + updateLanguage( + appLanguages[index]['locale']); + }, ), - onTap: () { - MyApp.updateAppState(context, - newLocale: appLanguages[index] - ['locale']); - updateLanguage( - appLanguages[index]['locale']); - }, - ), - ); - }, - ), - const SizedBox(height: 10), - ], - ); - }, + ); + }, + ), + const SizedBox(height: 10), + ], + ); + }, + ), ); }, ); @@ -812,41 +827,44 @@ class _SettingsPageState extends State { showModalBottomSheet( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, setState) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 15), - child: Text( - 'support'.tr, - style: context.textTheme.titleLarge?.copyWith( - fontSize: 20, + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: StatefulBuilder( + builder: (BuildContext context, setState) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Text( + 'support'.tr, + style: context.textTheme.titleLarge?.copyWith( + fontSize: 20, + ), ), ), - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.card), - text: 'DonationAlerts', - onPressed: () => urlLauncher( - 'https://www.donationalerts.com/r/darkmoonight'), - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.wallet), - text: 'ЮMoney', - onPressed: () => urlLauncher( - 'https://yoomoney.ru/to/4100117672775961'), - ), - const SizedBox(height: 10), - ], - ), - ); - }, + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.card), + text: 'DonationAlerts', + onPressed: () => urlLauncher( + 'https://www.donationalerts.com/r/darkmoonight'), + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.wallet), + text: 'ЮMoney', + onPressed: () => urlLauncher( + 'https://yoomoney.ru/to/4100117672775961'), + ), + const SizedBox(height: 10), + ], + ), + ); + }, + ), ); }, ); @@ -859,54 +877,57 @@ class _SettingsPageState extends State { showModalBottomSheet( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, setState) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20, vertical: 15), - child: Text( - 'groups'.tr, - style: context.textTheme.titleLarge?.copyWith( - fontSize: 20, + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: StatefulBuilder( + builder: (BuildContext context, setState) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 15), + child: Text( + 'groups'.tr, + style: context.textTheme.titleLarge?.copyWith( + fontSize: 20, + ), ), ), - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.voice_square), - text: 'Discord', - onPressed: () async { - final Uri url = - Uri.parse('https://discord.gg/JMMa9aHh8f'); - if (!await launchUrl(url, - mode: LaunchMode.externalApplication)) { - throw Exception('Could not launch $url'); - } - }, - ), - SettingCard( - elevation: 4, - icon: const Icon(Iconsax.message_square), - text: 'Telegram', - onPressed: () async { - final Uri url = - Uri.parse('https://t.me/darkmoonightX'); - if (!await launchUrl(url, - mode: LaunchMode.externalApplication)) { - throw Exception('Could not launch $url'); - } - }, - ), - const SizedBox(height: 10), - ], - ), - ); - }, + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.voice_square), + text: 'Discord', + onPressed: () async { + final Uri url = + Uri.parse('https://discord.gg/JMMa9aHh8f'); + if (!await launchUrl(url, + mode: LaunchMode.externalApplication)) { + throw Exception('Could not launch $url'); + } + }, + ), + SettingCard( + elevation: 4, + icon: const Icon(Iconsax.message_square), + text: 'Telegram', + onPressed: () async { + final Uri url = + Uri.parse('https://t.me/darkmoonightX'); + if (!await launchUrl(url, + mode: LaunchMode.externalApplication)) { + throw Exception('Could not launch $url'); + } + }, + ), + const SizedBox(height: 10), + ], + ), + ); + }, + ), ); }, ); diff --git a/lib/main.dart b/lib/main.dart index b8513eb..2f91b26 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:rain/app/modules/geolocation.dart'; import 'package:rain/app/modules/home.dart'; import 'package:rain/app/modules/onboarding.dart'; import 'package:rain/theme/theme.dart'; +import 'package:rain/utils/device_info.dart'; import 'package:time_machine/time_machine.dart'; import 'package:timezone/data/latest_all.dart' as tz; import 'package:timezone/timezone.dart' as tz; @@ -92,8 +93,7 @@ void main() async { ? isOnline.value = Future(() => false) : isOnline.value = InternetConnection().hasInternetAccess; }); - SystemChrome.setSystemUIOverlayStyle( - const SystemUiOverlayStyle(systemNavigationBarColor: Colors.black)); + DeviceFeature().init(); if (Platform.isAndroid) { Workmanager().initialize(callbackDispatcher, isInDebugMode: kDebugMode); await setOptimalDisplayMode(); @@ -284,34 +284,42 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { + final edgeToEdgeAvailable = DeviceFeature().isEdgeToEdgeAvailable(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + return GestureDetector( onTap: () => FocusManager.instance.primaryFocus?.unfocus(), child: DynamicColorBuilder( builder: (lightColorScheme, darkColorScheme) { - final lightMaterialTheme = - lightTheme(lightColorScheme?.surface, lightColorScheme); - final darkMaterialTheme = - darkTheme(darkColorScheme?.surface, darkColorScheme); - final darkMaterialThemeOled = darkTheme(oledColor, darkColorScheme); + final lightMaterialTheme = lightTheme( + lightColorScheme?.surface, lightColorScheme, edgeToEdgeAvailable); + final darkMaterialTheme = darkTheme( + darkColorScheme?.surface, darkColorScheme, edgeToEdgeAvailable); + final darkMaterialThemeOled = + darkTheme(oledColor, darkColorScheme, edgeToEdgeAvailable); return GetMaterialApp( themeMode: themeController.theme, theme: materialColor ? lightColorScheme != null ? lightMaterialTheme - : lightTheme(lightColor, colorSchemeLight) - : lightTheme(lightColor, colorSchemeLight), + : lightTheme( + lightColor, colorSchemeLight, edgeToEdgeAvailable) + : lightTheme(lightColor, colorSchemeLight, edgeToEdgeAvailable), darkTheme: amoledTheme ? materialColor ? darkColorScheme != null ? darkMaterialThemeOled - : darkTheme(oledColor, colorSchemeDark) - : darkTheme(oledColor, colorSchemeDark) + : darkTheme( + oledColor, colorSchemeDark, edgeToEdgeAvailable) + : darkTheme(oledColor, colorSchemeDark, edgeToEdgeAvailable) : materialColor ? darkColorScheme != null ? darkMaterialTheme - : darkTheme(darkColor, colorSchemeDark) - : darkTheme(darkColor, colorSchemeDark), + : darkTheme( + darkColor, colorSchemeDark, edgeToEdgeAvailable) + : darkTheme( + darkColor, colorSchemeDark, edgeToEdgeAvailable), localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, diff --git a/lib/theme/theme.dart b/lib/theme/theme.dart index f1a476d..c7af436 100644 --- a/lib/theme/theme.dart +++ b/lib/theme/theme.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:dynamic_color/dynamic_color.dart'; @@ -18,7 +19,8 @@ ColorScheme colorSchemeDark = ColorScheme.fromSeed( brightness: Brightness.dark, ); -ThemeData lightTheme(Color? color, ColorScheme? colorScheme) { +ThemeData lightTheme( + Color? color, ColorScheme? colorScheme, bool edgeToEdgeAvailable) { return baseLigth.copyWith( brightness: Brightness.light, colorScheme: colorScheme @@ -34,6 +36,16 @@ ThemeData lightTheme(Color? color, ColorScheme? colorScheme) { shadowColor: Colors.transparent, surfaceTintColor: Colors.transparent, elevation: 0, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.dark, + statusBarColor: Colors.transparent, + systemStatusBarContrastEnforced: false, + systemNavigationBarContrastEnforced: false, + systemNavigationBarDividerColor: Colors.transparent, + systemNavigationBarIconBrightness: Brightness.dark, + systemNavigationBarColor: + edgeToEdgeAvailable ? Colors.transparent : colorScheme?.surface, + ), ), primaryColor: color, canvasColor: color, @@ -74,7 +86,8 @@ ThemeData lightTheme(Color? color, ColorScheme? colorScheme) { ); } -ThemeData darkTheme(Color? color, ColorScheme? colorScheme) { +ThemeData darkTheme( + Color? color, ColorScheme? colorScheme, bool edgeToEdgeAvailable) { return baseDark.copyWith( brightness: Brightness.dark, colorScheme: colorScheme @@ -90,6 +103,16 @@ ThemeData darkTheme(Color? color, ColorScheme? colorScheme) { shadowColor: Colors.transparent, surfaceTintColor: Colors.transparent, elevation: 0, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.light, + statusBarColor: Colors.transparent, + systemStatusBarContrastEnforced: false, + systemNavigationBarContrastEnforced: false, + systemNavigationBarDividerColor: Colors.transparent, + systemNavigationBarIconBrightness: Brightness.light, + systemNavigationBarColor: + edgeToEdgeAvailable ? Colors.transparent : colorScheme?.surface, + ), ), primaryColor: color, canvasColor: color, diff --git a/lib/utils/device_info.dart b/lib/utils/device_info.dart new file mode 100644 index 0000000..61d8a93 --- /dev/null +++ b/lib/utils/device_info.dart @@ -0,0 +1,31 @@ +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; + +class DeviceFeature { + DeviceFeature._internal(); + + static final DeviceFeature _singleton = DeviceFeature._internal(); + + factory DeviceFeature() { + return _singleton; + } + + final _deviceInfoPlugin = DeviceInfoPlugin(); + + AndroidDeviceInfo? _androidDeviceInfo; + + Future init() async { + try { + _androidDeviceInfo = await _deviceInfoPlugin.androidInfo; + } catch (e) { + if (kDebugMode) { + print('Error initializing device info: $e'); + } + } + } + + bool isEdgeToEdgeAvailable() { + return _androidDeviceInfo != null && + _androidDeviceInfo!.version.sdkInt > 28; + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index ee92ad9..7c9d1ec 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,6 +6,7 @@ import FlutterMacOS import Foundation import connectivity_plus +import device_info_plus import dynamic_color import flutter_local_notifications import flutter_timezone @@ -17,6 +18,7 @@ import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin")) diff --git a/pubspec.lock b/pubspec.lock index db903bc..2c145dd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -233,6 +233,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91 + url: "https://pub.dev" + source: hosted + version: "10.1.0" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + url: "https://pub.dev" + source: hosted + version: "7.0.0" dio: dependency: "direct main" description: @@ -302,6 +318,15 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0" + flutter_glow: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: "7c5e1fd34583db4b40073add9ca329b03bf52a51" + url: "https://github.com/payam-zahedi/flutter-glow.git" + source: git + version: "0.3.1" flutter_hsvcolor_picker: dependency: "direct main" description: @@ -372,10 +397,10 @@ packages: dependency: "direct main" description: name: flutter_timezone - sha256: b7448ff8a9e1350606727e40b3f314aa798a6a1cc07127eba58f09b98a66f03f + sha256: f9c328f66d58cd2af8a0cbd2f84d0c211fda8b7332b5e458d9848bd9ec936120 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" flutter_web_plugins: dependency: transitive description: flutter @@ -393,10 +418,10 @@ packages: dependency: "direct main" description: name: freezed_annotation - sha256: f54946fdb1fa7b01f780841937b1a80783a20b393485f3f6cdf336fd6f4705f2 + sha256: f9f6597ac43cc262fa7d7f2e65259a6060c23a560525d1f2631be374540f2a9b url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.3" frontend_server_client: dependency: transitive description: @@ -625,18 +650,10 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - js_interop: - dependency: transitive - description: - name: js_interop - sha256: "7ec859c296958ccea34dc770504bd3ff4ae52fdd9e7eeb2bacc7081ad476a1f5" + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.0.1" + version: "0.7.1" json_annotation: dependency: "direct main" description: @@ -785,10 +802,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a + sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e" url: "https://pub.dev" source: hosted - version: "2.2.6" + version: "2.2.7" path_provider_foundation: dependency: transitive description: @@ -817,10 +834,10 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" petitparser: dependency: transitive description: @@ -1162,6 +1179,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.5.1" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" + url: "https://pub.dev" + source: hosted + version: "1.1.3" workmanager: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d18bce6..7e6b1ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,14 +29,18 @@ dependencies: google_fonts: ^6.2.1 url_launcher: ^6.3.0 time_machine: ^0.9.17 + flutter_glow: + git: + url: https://github.com/payam-zahedi/flutter-glow.git dynamic_color: ^1.7.0 path_provider: ^2.1.3 # quick_settings: ^1.0.1 json_annotation: ^4.9.0 - flutter_timezone: ^2.0.0 + flutter_timezone: ^2.0.1 + device_info_plus: ^10.1.0 package_info_plus: ^8.0.0 connectivity_plus: ^6.0.3 - freezed_annotation: ^2.4.2 + freezed_annotation: ^2.4.3 isar_flutter_libs: version: ^3.1.7 hosted: https://pub.isar-community.dev/