This commit is contained in:
Yoshi 2025-03-15 23:40:48 +03:00
parent d169f6237f
commit fb150421a6
62 changed files with 5006 additions and 4901 deletions

View file

@ -7,8 +7,8 @@ import 'package:rain/app/data/db.dart';
import 'package:rain/main.dart';
class WeatherAPI {
final Dio dio = Dio()
..options.baseUrl = 'https://api.open-meteo.com/v1/forecast?';
final Dio dio =
Dio()..options.baseUrl = 'https://api.open-meteo.com/v1/forecast?';
final Dio dioLocation = Dio();
static const String _weatherParams =
@ -41,14 +41,25 @@ class WeatherAPI {
}
}
Future<WeatherCard> getWeatherCard(double lat, double lon, String city,
String district, String timezone) async {
Future<WeatherCard> getWeatherCard(
double lat,
double lon,
String city,
String district,
String timezone,
) async {
final String urlWeather = _buildWeatherUrl(lat, lon);
try {
Response response = await dio.get(urlWeather);
WeatherDataApi weatherData = WeatherDataApi.fromJson(response.data);
return _mapWeatherDataToCard(
weatherData, lat, lon, city, district, timezone);
weatherData,
lat,
lon,
city,
district,
timezone,
);
} on DioException catch (e) {
if (kDebugMode) {
print(e);
@ -124,8 +135,14 @@ class WeatherAPI {
);
}
WeatherCard _mapWeatherDataToCard(WeatherDataApi weatherData, double lat,
double lon, String city, String district, String timezone) {
WeatherCard _mapWeatherDataToCard(
WeatherDataApi weatherData,
double lat,
double lon,
String city,
String district,
String timezone,
) {
return WeatherCard(
time: weatherData.hourly.time,
temperature2M: weatherData.hourly.temperature2M,

View file

@ -1,12 +1,11 @@
class CityApi {
CityApi({
required this.results,
});
CityApi({required this.results});
List<Result> results;
factory CityApi.fromJson(Map<String, dynamic> json) => CityApi(
results: json['results'] == null
results:
json['results'] == null
? List<Result>.empty()
: List<Result>.from(json['results'].map((x) => Result.fromJson(x))),
);

View file

@ -1,7 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
@ -53,15 +52,14 @@ class WeatherController extends GetxController {
@override
void onInit() {
weatherCards
.assignAll(isar.weatherCards.where().sortByIndex().findAllSync());
weatherCards.assignAll(
isar.weatherCards.where().sortByIndex().findAllSync(),
);
super.onInit();
}
Future<Position> determinePosition() async {
LocationPermission permission;
permission = await Geolocator.checkPermission();
Future<Position> _determinePosition() async {
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
@ -71,7 +69,8 @@ class WeatherController extends GetxController {
if (permission == LocationPermission.deniedForever) {
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
'Location permissions are permanently denied, we cannot request permissions.',
);
}
return await Geolocator.getCurrentPosition();
}
@ -80,25 +79,26 @@ class WeatherController extends GetxController {
if (settings.location) {
await getCurrentLocation();
} else {
if ((isar.locationCaches.where().findAllSync()).isNotEmpty) {
LocationCache locationCity =
(isar.locationCaches.where().findFirstSync())!;
await getLocation(locationCity.lat!, locationCity.lon!,
locationCity.district!, locationCity.city!);
final locationCity = isar.locationCaches.where().findFirstSync();
if (locationCity != null) {
await getLocation(
locationCity.lat!,
locationCity.lon!,
locationCity.district!,
locationCity.city!,
);
}
}
}
Future<void> getCurrentLocation() async {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!(await isOnline.value)) {
showSnackBar(content: 'no_inter'.tr);
await readCache();
return;
}
if (!serviceEnabled) {
if (!await Geolocator.isLocationServiceEnabled()) {
showSnackBar(
content: 'no_location'.tr,
onPressed: () => Geolocator.openLocationSettings(),
@ -107,70 +107,73 @@ class WeatherController extends GetxController {
return;
}
if ((isar.mainWeatherCaches.where().findAllSync()).isNotEmpty) {
if (isar.mainWeatherCaches.where().findAllSync().isNotEmpty) {
await readCache();
return;
}
Position position = await determinePosition();
List<Placemark> placemarks =
await placemarkFromCoordinates(position.latitude, position.longitude);
Placemark place = placemarks[0];
final position = await _determinePosition();
final placemarks = await placemarkFromCoordinates(
position.latitude,
position.longitude,
);
final place = placemarks[0];
_latitude.value = position.latitude;
_longitude.value = position.longitude;
_district.value = '${place.administrativeArea}';
_city.value = '${place.locality}';
_district.value = place.administrativeArea ?? '';
_city.value = place.locality ?? '';
_mainWeather.value =
await WeatherAPI().getWeatherData(_latitude.value, _longitude.value);
_mainWeather.value = await WeatherAPI().getWeatherData(
_latitude.value,
_longitude.value,
);
notificationCheck();
await writeCache();
await readCache();
}
Future<Map> getCurrentLocationSearch() async {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
double lat, lon;
String city, district;
if (!(await isOnline.value)) {
showSnackBar(content: 'no_inter'.tr);
}
if (!serviceEnabled) {
if (!await Geolocator.isLocationServiceEnabled()) {
showSnackBar(
content: 'no_location'.tr,
onPressed: () => Geolocator.openLocationSettings(),
);
}
Position position = await determinePosition();
List<Placemark> placemarks =
await placemarkFromCoordinates(position.latitude, position.longitude);
Placemark place = placemarks[0];
final position = await _determinePosition();
final placemarks = await placemarkFromCoordinates(
position.latitude,
position.longitude,
);
final place = placemarks[0];
lat = position.latitude;
lon = position.longitude;
city = '${place.administrativeArea}';
district = '${place.locality}';
Map location = {'lat': lat, 'lon': lon, 'city': city, 'district': district};
return location;
return {
'lat': position.latitude,
'lon': position.longitude,
'city': place.administrativeArea ?? '',
'district': place.locality ?? '',
};
}
Future<void> getLocation(double latitude, double longitude, String district,
String locality) async {
Future<void> getLocation(
double latitude,
double longitude,
String district,
String locality,
) async {
if (!(await isOnline.value)) {
showSnackBar(content: 'no_inter'.tr);
await readCache();
return;
}
if ((isar.mainWeatherCaches.where().findAllSync()).isNotEmpty) {
if (isar.mainWeatherCaches.where().findAllSync().isNotEmpty) {
await readCache();
return;
}
@ -180,11 +183,12 @@ class WeatherController extends GetxController {
_district.value = district;
_city.value = locality;
_mainWeather.value =
await WeatherAPI().getWeatherData(_latitude.value, _longitude.value);
_mainWeather.value = await WeatherAPI().getWeatherData(
_latitude.value,
_longitude.value,
);
notificationCheck();
await writeCache();
await readCache();
}
@ -201,10 +205,14 @@ class WeatherController extends GetxController {
_mainWeather.value = mainWeatherCache;
_location.value = locationCache;
hourOfDay.value =
getTime(_mainWeather.value.time!, _mainWeather.value.timezone!);
dayOfNow.value =
getDay(_mainWeather.value.timeDaily!, _mainWeather.value.timezone!);
hourOfDay.value = getTime(
_mainWeather.value.time!,
_mainWeather.value.timezone!,
);
dayOfNow.value = getDay(
_mainWeather.value.timeDaily!,
_mainWeather.value.timezone!,
);
if (Platform.isAndroid) {
Workmanager().registerPeriodicTask(
@ -235,16 +243,10 @@ class WeatherController extends GetxController {
);
isar.writeTxnSync(() {
final mainWeatherCachesIsEmpty =
(isar.mainWeatherCaches.where().findAllSync()).isEmpty;
final locationCachesIsEmpty =
(isar.locationCaches.where().findAllSync()).isEmpty;
if (mainWeatherCachesIsEmpty) {
if (isar.mainWeatherCaches.where().findAllSync().isEmpty) {
isar.mainWeatherCaches.putSync(_mainWeather.value);
}
if (locationCachesIsEmpty) {
if (isar.locationCaches.where().findAllSync().isEmpty) {
isar.locationCaches.putSync(locationCaches);
}
});
@ -261,7 +263,7 @@ class WeatherController extends GetxController {
.timestampLessThan(cacheExpiry)
.deleteAllSync();
});
if ((isar.mainWeatherCaches.where().findAllSync()).isEmpty) {
if (isar.mainWeatherCaches.where().findAllSync().isEmpty) {
await flutterLocalNotificationsPlugin.cancelAll();
}
}
@ -271,31 +273,39 @@ class WeatherController extends GetxController {
return;
}
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
final serviceEnabled = await Geolocator.isLocationServiceEnabled();
await flutterLocalNotificationsPlugin.cancelAll();
isar.writeTxnSync(() {
if (!settings.location) {
isar.mainWeatherCaches.where().deleteAllSync();
}
if ((settings.location && serviceEnabled) || changeCity) {
if (settings.location && serviceEnabled || changeCity) {
isar.mainWeatherCaches.where().deleteAllSync();
isar.locationCaches.where().deleteAllSync();
}
});
}
// Card Weather
Future<void> addCardWeather(
double latitude, double longitude, String city, String district) async {
double latitude,
double longitude,
String city,
String district,
) async {
if (!(await isOnline.value)) {
showSnackBar(content: 'no_inter'.tr);
return;
}
String tz = tzmap.latLngToTimezoneString(latitude, longitude);
_weatherCard.value = await WeatherAPI()
.getWeatherCard(latitude, longitude, city, district, tz);
final tz = tzmap.latLngToTimezoneString(latitude, longitude);
_weatherCard.value = await WeatherAPI().getWeatherCard(
latitude,
longitude,
city,
district,
tz,
);
isar.writeTxnSync(() {
weatherCards.add(_weatherCard.value);
isar.weatherCards.putSync(_weatherCard.value);
@ -303,7 +313,8 @@ class WeatherController extends GetxController {
}
Future<void> updateCacheCard(bool refresh) async {
List<WeatherCard> weatherCard = refresh
final weatherCard =
refresh
? isar.weatherCards.where().sortByIndex().findAllSync()
: isar.weatherCards
.filter()
@ -311,13 +322,18 @@ class WeatherController extends GetxController {
.sortByIndex()
.findAllSync();
if ((!(await isOnline.value)) || weatherCard.isEmpty) {
if (!(await isOnline.value) || weatherCard.isEmpty) {
return;
}
for (var oldCard in weatherCard) {
var updatedCard = await WeatherAPI().getWeatherCard(oldCard.lat!,
oldCard.lon!, oldCard.city!, oldCard.district!, oldCard.timezone!);
final updatedCard = await WeatherAPI().getWeatherCard(
oldCard.lat!,
oldCard.lon!,
oldCard.city!,
oldCard.district!,
oldCard.timezone!,
);
isar.writeTxnSync(() {
oldCard
..time = updatedCard.time
@ -358,8 +374,8 @@ class WeatherController extends GetxController {
isar.weatherCards.putSync(oldCard);
var newCard = oldCard;
int oldIdx = weatherCard.indexOf(oldCard);
final newCard = oldCard;
final oldIdx = weatherCard.indexOf(oldCard);
weatherCards[oldIdx] = newCard;
weatherCards.refresh();
});
@ -428,35 +444,26 @@ class WeatherController extends GetxController {
}
int getTime(List<String> time, String timezone) {
int getTime = 0;
for (var i = 0; i < time.length; i++) {
if (tz.TZDateTime.now(tz.getLocation(timezone)).hour ==
DateTime.parse(time[i]).hour &&
tz.TZDateTime.now(tz.getLocation(timezone)).day ==
DateTime.parse(time[i]).day) {
getTime = i;
}
}
return getTime;
return time.indexWhere((t) {
final dateTime = DateTime.parse(t);
return tz.TZDateTime.now(tz.getLocation(timezone)).hour ==
dateTime.hour &&
tz.TZDateTime.now(tz.getLocation(timezone)).day == dateTime.day;
});
}
int getDay(List<DateTime> time, String timezone) {
int getDay = 0;
for (var i = 0; i < time.length; i++) {
if (tz.TZDateTime.now(tz.getLocation(timezone)).day == time[i].day) {
getDay = i;
}
}
return getDay;
return time.indexWhere(
(t) => tz.TZDateTime.now(tz.getLocation(timezone)).day == t.day,
);
}
TimeOfDay timeConvert(String normTime) {
int hh = 0;
if (normTime.endsWith('PM')) hh = 12;
normTime = normTime.split(' ')[0];
final hh = normTime.endsWith('PM') ? 12 : 0;
final timeParts = normTime.split(' ')[0].split(':');
return TimeOfDay(
hour: hh + int.parse(normTime.split(':')[0]) % 24,
minute: int.parse(normTime.split(':')[1]) % 60,
hour: hh + int.parse(timeParts[0]) % 24,
minute: int.parse(timeParts[1]) % 60,
);
}
@ -464,8 +471,8 @@ class WeatherController extends GetxController {
final directory = await getTemporaryDirectory();
final imagePath = '${directory.path}/$icon';
final ByteData data = await rootBundle.load('assets/images/$icon');
final List<int> bytes = data.buffer.asUint8List();
final data = await rootBundle.load('assets/images/$icon');
final bytes = data.buffer.asUint8List();
await File(imagePath).writeAsBytes(bytes);
@ -473,12 +480,12 @@ class WeatherController extends GetxController {
}
void notification(MainWeatherCache mainWeatherCache) async {
DateTime now = DateTime.now();
int startHour = timeConvert(timeStart).hour;
int endHour = timeConvert(timeEnd).hour;
final now = DateTime.now();
final startHour = timeConvert(timeStart).hour;
final endHour = timeConvert(timeEnd).hour;
for (var i = 0; i < mainWeatherCache.time!.length; i += timeRange) {
DateTime notificationTime = DateTime.parse(mainWeatherCache.time![i]);
final notificationTime = DateTime.parse(mainWeatherCache.time![i]);
if (notificationTime.isAfter(now) &&
notificationTime.hour >= startHour &&
@ -505,7 +512,7 @@ class WeatherController extends GetxController {
void notificationCheck() async {
if (settings.notifications) {
final List<PendingNotificationRequest> pendingNotificationRequests =
final pendingNotificationRequests =
await flutterLocalNotificationsPlugin.pendingNotificationRequests();
if (pendingNotificationRequests.isEmpty) {
notification(_mainWeather.value);
@ -513,7 +520,7 @@ class WeatherController extends GetxController {
}
}
void reorder(oldIndex, newIndex) {
void reorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
@ -533,15 +540,11 @@ class WeatherController extends GetxController {
isar.settings.putSync(settings);
});
return Future.wait<bool?>([
HomeWidget.saveWidgetData(
'background_color',
color,
),
final results = await Future.wait<bool?>([
HomeWidget.saveWidgetData('background_color', color),
HomeWidget.updateWidget(androidName: androidWidgetName),
]).then((value) {
return !value.contains(false);
});
]);
return !results.contains(false);
}
Future<bool> updateWidgetTextColor(String color) async {
@ -550,15 +553,11 @@ class WeatherController extends GetxController {
isar.settings.putSync(settings);
});
return Future.wait<bool?>([
HomeWidget.saveWidgetData(
'text_color',
color,
),
final results = await Future.wait<bool?>([
HomeWidget.saveWidgetData('text_color', color),
HomeWidget.updateWidget(androidName: androidWidgetName),
]).then((value) {
return !value.contains(false);
});
]);
return !results.contains(false);
}
Future<bool> updateWidget() async {
@ -573,34 +572,36 @@ class WeatherController extends GetxController {
WeatherCardSchema,
], directory: (await getApplicationSupportDirectory()).path);
MainWeatherCache? mainWeatherCache;
mainWeatherCache = isarWidget.mainWeatherCaches.where().findFirstSync();
final mainWeatherCache =
isarWidget.mainWeatherCaches.where().findFirstSync();
if (mainWeatherCache == null) return false;
int hour = getTime(mainWeatherCache.time!, mainWeatherCache.timezone!);
int day = getDay(mainWeatherCache.timeDaily!, mainWeatherCache.timezone!);
final hour = getTime(mainWeatherCache.time!, mainWeatherCache.timezone!);
final day = getDay(mainWeatherCache.timeDaily!, mainWeatherCache.timezone!);
return Future.wait<bool?>([
final results = await Future.wait<bool?>([
HomeWidget.saveWidgetData(
'weather_icon',
await getLocalImagePath(StatusWeather().getImageNotification(
await getLocalImagePath(
StatusWeather().getImageNotification(
mainWeatherCache.weathercode![hour],
mainWeatherCache.time![hour],
mainWeatherCache.sunrise![day],
mainWeatherCache.sunset![day],
))),
),
),
),
HomeWidget.saveWidgetData(
'weather_degree',
'${mainWeatherCache.temperature2M?[hour].round()}°',
),
HomeWidget.updateWidget(androidName: androidWidgetName),
]).then((value) {
return !value.contains(false);
});
]);
return !results.contains(false);
}
void urlLauncher(String uri) async {
final Uri url = Uri.parse(uri);
final url = Uri.parse(uri);
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
throw Exception('Could not launch $url');
}

View file

@ -152,12 +152,7 @@ class LocationCache {
String? city;
String? district;
LocationCache({
this.lat,
this.lon,
this.city,
this.district,
});
LocationCache({this.lat, this.lon, this.city, this.district});
Map<String, dynamic> toJson() => {
'id': id,
@ -304,8 +299,9 @@ class WeatherCard {
time: List<String>.from(json['time'] ?? []),
weathercode: List<int>.from(json['weathercode'] ?? []),
temperature2M: List<double>.from(json['temperature2M'] ?? []),
apparentTemperature:
List<double?>.from(json['apparentTemperature'] ?? []),
apparentTemperature: List<double?>.from(
json['apparentTemperature'] ?? [],
),
relativehumidity2M: List<int?>.from(json['relativehumidity2M'] ?? []),
precipitation: List<double>.from(json['precipitation'] ?? []),
rain: List<double?>.from(json['rain'] ?? []),
@ -318,26 +314,31 @@ class WeatherCard {
cloudcover: List<int?>.from(json['cloudcover'] ?? []),
uvIndex: List<double?>.from(json['uvIndex'] ?? []),
dewpoint2M: List<double?>.from(json['dewpoint2M'] ?? []),
precipitationProbability:
List<int?>.from(json['precipitationProbability'] ?? []),
precipitationProbability: List<int?>.from(
json['precipitationProbability'] ?? [],
),
shortwaveRadiation: List<double?>.from(json['shortwaveRadiation'] ?? []),
timeDaily: List<DateTime>.from(json['timeDaily'] ?? []),
weathercodeDaily: List<int?>.from(json['weathercodeDaily'] ?? []),
temperature2MMax: List<double?>.from(json['temperature2MMax'] ?? []),
temperature2MMin: List<double?>.from(json['temperature2MMin'] ?? []),
apparentTemperatureMax:
List<double?>.from(json['apparentTemperatureMax'] ?? []),
apparentTemperatureMin:
List<double?>.from(json['apparentTemperatureMin'] ?? []),
apparentTemperatureMax: List<double?>.from(
json['apparentTemperatureMax'] ?? [],
),
apparentTemperatureMin: List<double?>.from(
json['apparentTemperatureMin'] ?? [],
),
windspeed10MMax: List<double?>.from(json['windspeed10MMax'] ?? []),
windgusts10MMax: List<double?>.from(json['windgusts10MMax'] ?? []),
uvIndexMax: List<double?>.from(json['uvIndexMax'] ?? []),
rainSum: List<double?>.from(json['rainSum'] ?? []),
winddirection10MDominant:
List<int?>.from(json['winddirection10MDominant'] ?? []),
winddirection10MDominant: List<int?>.from(
json['winddirection10MDominant'] ?? [],
),
precipitationSum: List<double?>.from(json['precipitationSum'] ?? []),
precipitationProbabilityMax:
List<int?>.from(json['precipitationProbabilityMax'] ?? []),
precipitationProbabilityMax: List<int?>.from(
json['precipitationProbabilityMax'] ?? [],
),
sunrise: List<String>.from(json['sunrise'] ?? []),
sunset: List<String>.from(json['sunset'] ?? []),
lat: json['lat'],

View file

@ -15,10 +15,7 @@ import 'package:rain/app/ui/widgets/text_form.dart';
import 'package:rain/main.dart';
class SelectGeolocation extends StatefulWidget {
const SelectGeolocation({
super.key,
required this.isStart,
});
const SelectGeolocation({super.key, required this.isStart});
final bool isStart;
@override
@ -94,16 +91,14 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
resizeToAvoidBottomInset: true,
appBar: AppBar(
centerTitle: true,
leading: widget.isStart
leading:
widget.isStart
? null
: IconButton(
onPressed: () {
Get.back();
},
icon: const Icon(
IconsaxPlusLinear.arrow_left_3,
size: 20,
),
icon: const Icon(IconsaxPlusLinear.arrow_left_3, size: 20),
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
@ -131,7 +126,8 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10),
horizontal: 10,
),
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(20),
@ -150,7 +146,8 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
initialZoom: 3,
interactionOptions:
const InteractionOptions(
flags: InteractiveFlag.all &
flags:
InteractiveFlag.all &
~InteractiveFlag.rotate,
),
cameraConstraint:
@ -160,9 +157,11 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
const LatLng(90, 180),
),
),
onLongPress: (tapPosition, point) =>
fillMap(point.latitude,
point.longitude),
onLongPress:
(tapPosition, point) => fillMap(
point.latitude,
point.longitude,
),
),
children: [
if (_isDarkMode)
@ -177,9 +176,11 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
attributions: [
TextSourceAttribution(
'OpenStreetMap contributors',
onTap: () => weatherController
onTap:
() => weatherController
.urlLauncher(
'https://openstreetmap.org/copyright'),
'https://openstreetmap.org/copyright',
),
),
],
),
@ -189,8 +190,12 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
),
),
Padding(
padding:
const EdgeInsets.fromLTRB(10, 15, 10, 5),
padding: const EdgeInsets.fromLTRB(
10,
15,
10,
5,
),
child: Text(
'searchMethod'.tr,
style: context.theme.textTheme.bodyLarge
@ -204,42 +209,54 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
child: RawAutocomplete<Result>(
focusNode: _focusNode,
textEditingController: _controller,
fieldViewBuilder: (BuildContext context,
fieldViewBuilder: (
BuildContext context,
TextEditingController
fieldTextEditingController,
FocusNode fieldFocusNode,
VoidCallback onFieldSubmitted) {
VoidCallback onFieldSubmitted,
) {
return MyTextForm(
elevation: kTextFieldElevation,
labelText: 'search'.tr,
type: TextInputType.text,
icon: const Icon(IconsaxPlusLinear
.global_search),
icon: const Icon(
IconsaxPlusLinear.global_search,
),
controller: _controller,
margin: const EdgeInsets.only(
left: 10, right: 10, top: 10),
left: 10,
right: 10,
top: 10,
),
focusNode: _focusNode,
);
},
optionsBuilder: (TextEditingValue
textEditingValue) {
optionsBuilder: (
TextEditingValue textEditingValue,
) {
if (textEditingValue.text.isEmpty) {
return const Iterable<
Result>.empty();
Result
>.empty();
}
return WeatherAPI().getCity(
textEditingValue.text, locale);
textEditingValue.text,
locale,
);
},
onSelected: (Result selection) =>
onSelected:
(Result selection) =>
fillController(selection),
displayStringForOption: (Result
option) =>
displayStringForOption:
(Result option) =>
'${option.name}, ${option.admin1}',
optionsViewBuilder:
(BuildContext context,
optionsViewBuilder: (
BuildContext context,
AutocompleteOnSelected<Result>
onSelected,
Iterable<Result> options) {
Iterable<Result> options,
) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
@ -255,19 +272,24 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: options.length,
itemBuilder:
(BuildContext context,
int index) {
itemBuilder: (
BuildContext context,
int index,
) {
final Result option =
options
.elementAt(index);
options.elementAt(
index,
);
return InkWell(
onTap: () =>
onSelected(option),
onTap:
() => onSelected(
option,
),
child: ListTile(
title: Text(
'${option.name}, ${option.admin1}',
style: context
style:
context
.textTheme
.labelLarge,
),
@ -292,47 +314,53 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
child: IconButton(
onPressed: () async {
bool serviceEnabled =
await Geolocator
.isLocationServiceEnabled();
await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
if (!context.mounted) return;
await showAdaptiveDialog(
context: context,
builder:
(BuildContext context) {
builder: (
BuildContext context,
) {
return AlertDialog.adaptive(
title: Text(
'location'.tr,
style: context
.textTheme.titleLarge,
style:
context
.textTheme
.titleLarge,
),
content: Text(
'no_location'.tr,
style: context.textTheme
style:
context
.textTheme
.titleMedium,
),
actions: [
TextButton(
onPressed: () =>
Get.back(
result: false),
onPressed:
() => Get.back(
result: false,
),
child: Text(
'cancel'.tr,
style: context
.textTheme
.titleMedium
?.copyWith(
color: Colors
color:
Colors
.blueAccent,
),
),
),
TextButton(
onPressed: () {
Geolocator
.openLocationSettings();
Geolocator.openLocationSettings();
Get.back(
result: true);
result: true,
);
},
child: Text(
'settings'.tr,
@ -340,8 +368,10 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
.textTheme
.titleMedium
?.copyWith(
color: Colors
.green),
color:
Colors
.green,
),
),
),
],
@ -380,8 +410,9 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
if (value == null || value.isEmpty) {
return 'validateValue'.tr;
}
double? numericValue =
double.tryParse(value);
double? numericValue = double.tryParse(
value,
);
if (numericValue == null) {
return 'validateNumber'.tr;
}
@ -407,8 +438,9 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
if (value == null || value.isEmpty) {
return 'validateValue'.tr;
}
double? numericValue =
double.tryParse(value);
double? numericValue = double.tryParse(
value,
);
if (numericValue == null) {
return 'validateNumber'.tr;
}
@ -424,10 +456,14 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
controller: _controllerCity,
labelText: 'city'.tr,
type: TextInputType.name,
icon:
const Icon(IconsaxPlusLinear.building_3),
icon: const Icon(
IconsaxPlusLinear.building_3,
),
margin: const EdgeInsets.only(
left: 10, right: 10, top: 10),
left: 10,
right: 10,
top: 10,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'validateName'.tr;
@ -442,7 +478,10 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
type: TextInputType.streetAddress,
icon: const Icon(IconsaxPlusLinear.global),
margin: const EdgeInsets.only(
left: 10, right: 10, top: 10),
left: 10,
right: 10,
top: 10,
),
),
const Gap(20),
],
@ -453,8 +492,10 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
child: MyTextButton(
buttonName: 'done'.tr,
onPressed: () async {
@ -472,8 +513,10 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
_controllerCity.text,
);
widget.isStart
? Get.off(() => const HomePage(),
transition: Transition.downToUp)
? Get.off(
() => const HomePage(),
transition: Transition.downToUp,
)
: Get.back();
} catch (error) {
Future.error(error);
@ -487,9 +530,7 @@ class _SelectGeolocationState extends State<SelectGeolocation> {
if (isLoading)
BackdropFilter(
filter: ImageFilter.blur(sigmaY: 3, sigmaX: 3),
child: const Center(
child: CircularProgressIndicator(),
),
child: const Center(child: CircularProgressIndicator()),
),
],
),

View file

@ -103,18 +103,18 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
leading: switch (tabIndex) {
0 => IconButton(
onPressed: () {
Get.to(() => const SelectGeolocation(isStart: false),
transition: Transition.downToUp);
Get.to(
() => const SelectGeolocation(isStart: false),
transition: Transition.downToUp,
);
},
icon: const Icon(
IconsaxPlusLinear.global_search,
size: 18,
),
icon: const Icon(IconsaxPlusLinear.global_search, size: 18),
),
int() => null,
},
title: switch (tabIndex) {
0 => visible
0 =>
visible
? RawAutocomplete<Result>(
focusNode: _focusNode,
textEditingController: _controller,
@ -123,17 +123,17 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
controller: _controller,
focusNode: _focusNode,
style: labelLarge?.copyWith(fontSize: 16),
decoration: InputDecoration(
hintText: 'search'.tr,
),
decoration: InputDecoration(hintText: 'search'.tr),
);
},
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text.isEmpty) {
return const Iterable<Result>.empty();
}
return WeatherAPI()
.getCity(textEditingValue.text, locale);
return WeatherAPI().getCity(
textEditingValue.text,
locale,
);
},
onSelected: (Result selection) async {
await weatherController.deleteAll(true);
@ -148,11 +148,13 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
_focusNode.unfocus();
setState(() {});
},
displayStringForOption: (Result option) =>
'${option.name}, ${option.admin1}',
optionsViewBuilder: (BuildContext context,
displayStringForOption:
(Result option) => '${option.name}, ${option.admin1}',
optionsViewBuilder: (
BuildContext context,
AutocompleteOnSelected<Result> onSelected,
Iterable<Result> options) {
Iterable<Result> options,
) {
return Align(
alignment: Alignment.topLeft,
child: Material(
@ -165,8 +167,9 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final Result option =
options.elementAt(index);
final Result option = options.elementAt(
index,
);
return InkWell(
onTap: () => onSelected(option),
child: ListTile(
@ -183,8 +186,7 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
);
},
)
: Obx(
() {
: Obx(() {
final location = weatherController.location;
final city = location.city;
final district = location.district;
@ -196,7 +198,8 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
? district
: city == district
? city
: '$city' ', $district'
: '$city'
', $district'
: settings.location
? 'search'.tr
: (isar.locationCaches.where().findAllSync())
@ -205,25 +208,13 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
: 'searchCity'.tr,
style: textStyle,
);
},
),
1 => Text(
'cities'.tr,
style: textStyle,
),
2 => settings.hideMap
? Text(
'settings_full'.tr,
style: textStyle,
)
: Text(
'map'.tr,
style: textStyle,
),
3 => Text(
'settings_full'.tr,
style: textStyle,
),
}),
1 => Text('cities'.tr, style: textStyle),
2 =>
settings.hideMap
? Text('settings_full'.tr, style: textStyle)
: Text('map'.tr, style: textStyle),
3 => Text('settings_full'.tr, style: textStyle),
int() => null,
},
actions: switch (tabIndex) {
@ -245,16 +236,13 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
: IconsaxPlusLinear.search_normal_1,
size: 18,
),
)
),
],
int() => null,
},
),
body: SafeArea(
child: TabBarView(
controller: tabController,
children: pages,
),
child: TabBarView(controller: tabController, children: pages),
),
bottomNavigationBar: NavigationBar(
onDestinationSelected: (int index) => changeTabIndex(index),
@ -283,17 +271,18 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
),
],
),
floatingActionButton: tabIndex == 1
floatingActionButton:
tabIndex == 1
? FloatingActionButton(
onPressed: () => showModalBottomSheet(
onPressed:
() => showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: false,
builder: (BuildContext context) => const CreatePlace(),
),
child: const Icon(
IconsaxPlusLinear.add,
builder:
(BuildContext context) => const CreatePlace(),
),
child: const Icon(IconsaxPlusLinear.add),
)
: null,
),

View file

@ -35,9 +35,7 @@ class _MainPageState extends State<MainPage> {
if (weatherController.isLoading.isTrue) {
return ListView(
children: const [
MyShimmer(
hight: 200,
),
MyShimmer(hight: 200),
MyShimmer(
hight: 130,
edgeInsetsMargin: EdgeInsets.symmetric(vertical: 15),
@ -53,7 +51,7 @@ class _MainPageState extends State<MainPage> {
MyShimmer(
hight: 450,
edgeInsetsMargin: EdgeInsets.only(bottom: 15),
)
),
],
);
}
@ -82,8 +80,10 @@ class _MainPageState extends State<MainPage> {
Card(
margin: const EdgeInsets.only(bottom: 15),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
child: SizedBox(
height: 135,
child: ScrollablePositionedList.separated(
@ -115,8 +115,12 @@ class _MainPageState extends State<MainPage> {
vertical: 5,
),
decoration: BoxDecoration(
color: i == hourOfDay
? context.theme.colorScheme.secondaryContainer
color:
i == hourOfDay
? context
.theme
.colorScheme
.secondaryContainer
: Colors.transparent,
borderRadius: const BorderRadius.all(
Radius.circular(20),
@ -136,10 +140,7 @@ class _MainPageState extends State<MainPage> {
),
),
),
SunsetSunrise(
timeSunrise: sunrise,
timeSunset: sunset,
),
SunsetSunrise(timeSunrise: sunrise, timeSunset: sunset),
DescContainer(
humidity: mainWeather.relativehumidity2M?[hourOfDay],
wind: mainWeather.windspeed10M?[hourOfDay],
@ -162,13 +163,12 @@ class _MainPageState extends State<MainPage> {
),
DailyContainer(
weatherData: weatherCard,
onTap: () => Get.to(
() => DailyCardList(
weatherData: weatherCard,
),
onTap:
() => Get.to(
() => DailyCardList(weatherData: weatherCard),
transition: Transition.downToUp,
),
)
),
],
);
}),

View file

@ -66,10 +66,9 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
_offsetAnimation = Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
));
).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
super.initState();
}
@ -114,25 +113,26 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
_focusNode.unfocus();
}
Widget _buidStyleMarkers(int weathercode, String time, String sunrise,
String sunset, double temperature2M) {
Widget _buidStyleMarkers(
int weathercode,
String time,
String sunrise,
String sunset,
double temperature2M,
) {
return Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
statusWeather.getImageNow(
weathercode,
time,
sunrise,
sunset,
),
statusWeather.getImageNow(weathercode, time, sunrise, sunset),
scale: 18,
),
const MaxGap(5),
Text(
statusData
.getDegree(roundDegree ? temperature2M.round() : temperature2M),
statusData.getDegree(
roundDegree ? temperature2M.round() : temperature2M,
),
style: context.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.bold,
fontSize: 16,
@ -144,7 +144,10 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
}
Marker _buildMainLocationMarker(
WeatherCard weatherCard, int hourOfDay, int dayOfNow) {
WeatherCard weatherCard,
int hourOfDay,
int dayOfNow,
) {
return Marker(
height: 50,
width: 100,
@ -171,15 +174,25 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
onTap: () => _onMarkerTap(weatherCardList),
child: _buidStyleMarkers(
weatherCardList.weathercode![weatherController.getTime(
weatherCardList.time!, weatherCardList.timezone!)],
weatherCardList.time!,
weatherCardList.timezone!,
)],
weatherCardList.time![weatherController.getTime(
weatherCardList.time!, weatherCardList.timezone!)],
weatherCardList.time!,
weatherCardList.timezone!,
)],
weatherCardList.sunrise![weatherController.getDay(
weatherCardList.timeDaily!, weatherCardList.timezone!)],
weatherCardList.timeDaily!,
weatherCardList.timezone!,
)],
weatherCardList.sunset![weatherController.getDay(
weatherCardList.timeDaily!, weatherCardList.timezone!)],
weatherCardList.timeDaily!,
weatherCardList.timezone!,
)],
weatherCardList.temperature2M![weatherController.getTime(
weatherCardList.time!, weatherCardList.timezone!)],
weatherCardList.time!,
weatherCardList.timezone!,
)],
),
),
);
@ -201,7 +214,8 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
? SlideTransition(
position: _offsetAnimation,
child: GestureDetector(
onTap: () => Get.to(
onTap:
() => Get.to(
() => PlaceInfo(weatherCard: _selectedWeatherCard!),
transition: Transition.downToUp,
),
@ -261,11 +275,13 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
),
),
onTap: (_, __) => _hideCard(),
onLongPress: (tapPosition, point) => showModalBottomSheet(
onLongPress:
(tapPosition, point) => showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: false,
builder: (BuildContext context) => CreatePlace(
builder:
(BuildContext context) => CreatePlace(
latitude: '${point.latitude}',
longitude: '${point.longitude}',
),
@ -290,8 +306,10 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
attributions: [
TextSourceAttribution(
'OpenStreetMap contributors',
onTap: () => weatherController
.urlLauncher('https://openstreetmap.org/copyright'),
onTap:
() => weatherController.urlLauncher(
'https://openstreetmap.org/copyright',
),
),
],
),
@ -305,14 +323,15 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
dayOfNow,
);
final cardMarkers = weatherController.weatherCards
.map((weatherCardList) =>
_buildCardMarker(weatherCardList))
final cardMarkers =
weatherController.weatherCards
.map(
(weatherCardList) =>
_buildCardMarker(weatherCardList),
)
.toList();
return MarkerLayer(
markers: [mainMarker, ...cardMarkers],
);
return MarkerLayer(markers: [mainMarker, ...cardMarkers]);
}),
ExpandableFab(
key: _fabKey,
@ -331,23 +350,31 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
FloatingActionButton(
heroTag: null,
child: const Icon(IconsaxPlusLinear.home_2),
onPressed: () => _resetMapOrientation(
center:
LatLng(mainLocation.lat!, mainLocation.lon!),
zoom: 8),
onPressed:
() => _resetMapOrientation(
center: LatLng(
mainLocation.lat!,
mainLocation.lon!,
),
zoom: 8,
),
),
FloatingActionButton(
heroTag: null,
child: const Icon(IconsaxPlusLinear.search_zoom_out_1),
onPressed: () => _animatedMapController.animatedZoomOut(
customId: _useTransformer ? _useTransformerId : null,
onPressed:
() => _animatedMapController.animatedZoomOut(
customId:
_useTransformer ? _useTransformerId : null,
),
),
FloatingActionButton(
heroTag: null,
child: const Icon(IconsaxPlusLinear.search_zoom_in),
onPressed: () => _animatedMapController.animatedZoomIn(
customId: _useTransformer ? _useTransformerId : null,
onPressed:
() => _animatedMapController.animatedZoomIn(
customId:
_useTransformer ? _useTransformerId : null,
),
),
],
@ -363,10 +390,12 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
RawAutocomplete<Result>(
focusNode: _focusNode,
textEditingController: _controllerSearch,
fieldViewBuilder: (BuildContext context,
fieldViewBuilder: (
BuildContext context,
TextEditingController fieldTextEditingController,
FocusNode fieldFocusNode,
VoidCallback onFieldSubmitted) {
VoidCallback onFieldSubmitted,
) {
return MyTextForm(
labelText: 'search'.tr,
type: TextInputType.text,
@ -375,7 +404,8 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
margin: const EdgeInsets.only(left: 10, right: 10, top: 10),
focusNode: _focusNode,
onChanged: (value) => setState(() {}),
iconButton: _controllerSearch.text.isNotEmpty
iconButton:
_controllerSearch.text.isNotEmpty
? IconButton(
onPressed: () {
_controllerSearch.clear();
@ -397,18 +427,24 @@ class _MapPageState extends State<MapPage> with TickerProviderStateMixin {
},
onSelected: (Result selection) {
_animatedMapController.mapController.move(
LatLng(selection.latitude, selection.longitude), 14);
LatLng(selection.latitude, selection.longitude),
14,
);
_controllerSearch.clear();
_focusNode.unfocus();
},
displayStringForOption: (Result option) =>
'${option.name}, ${option.admin1}',
optionsViewBuilder: (BuildContext context,
displayStringForOption:
(Result option) => '${option.name}, ${option.admin1}',
optionsViewBuilder: (
BuildContext context,
AutocompleteOnSelected<Result> onSelected,
Iterable<Result> options) {
Iterable<Result> options,
) {
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
child: Align(
alignment: Alignment.topCenter,
child: Material(

View file

@ -32,8 +32,10 @@ class _OnBordingState extends State<OnBording> {
void onBoardHome() {
settings.onboard = true;
isar.writeTxnSync(() => isar.settings.putSync(settings));
Get.off(() => const SelectGeolocation(isStart: true),
transition: Transition.downToUp);
Get.off(
() => const SelectGeolocation(isStart: true),
transition: Transition.downToUp,
);
}
@override
@ -52,7 +54,8 @@ class _OnBordingState extends State<OnBording> {
pageIndex = index;
});
},
itemBuilder: (context, index) => OnboardContent(
itemBuilder:
(context, index) => OnboardContent(
image: data[index].image,
title: data[index].title,
description: data[index].description,
@ -67,7 +70,8 @@ class _OnBordingState extends State<OnBording> {
(index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: DotIndicator(isActive: index == pageIndex),
)),
),
),
],
),
Padding(
@ -84,7 +88,7 @@ class _OnBordingState extends State<OnBording> {
);
},
),
)
),
],
),
),
@ -93,10 +97,7 @@ class _OnBordingState extends State<OnBording> {
}
class DotIndicator extends StatelessWidget {
const DotIndicator({
super.key,
this.isActive = false,
});
const DotIndicator({super.key, this.isActive = false});
final bool isActive;
@ -107,7 +108,8 @@ class DotIndicator extends StatelessWidget {
height: 8,
width: 8,
decoration: BoxDecoration(
color: isActive
color:
isActive
? context.theme.colorScheme.secondary
: context.theme.colorScheme.secondaryContainer,
shape: BoxShape.circle,
@ -130,15 +132,18 @@ final List<Onboard> data = [
Onboard(
image: 'assets/icons/Rain.png',
title: 'name'.tr,
description: 'description'.tr),
description: 'description'.tr,
),
Onboard(
image: 'assets/icons/Design.png',
title: 'name2'.tr,
description: 'description2'.tr),
description: 'description2'.tr,
),
Onboard(
image: 'assets/icons/Team.png',
title: 'name3'.tr,
description: 'description3'.tr),
description: 'description3'.tr,
),
];
class OnboardContent extends StatelessWidget {
@ -158,14 +163,12 @@ class OnboardContent extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
image,
scale: 5,
),
Image.asset(image, scale: 5),
Text(
title,
style: context.textTheme.titleLarge
?.copyWith(fontWeight: FontWeight.w600),
style: context.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
),
),
const Gap(10),
SizedBox(

View file

@ -12,10 +12,7 @@ import 'package:rain/app/ui/widgets/weather/sunset_sunrise.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
class PlaceInfo extends StatefulWidget {
const PlaceInfo({
super.key,
required this.weatherCard,
});
const PlaceInfo({super.key, required this.weatherCard});
final WeatherCard weatherCard;
@override
@ -37,10 +34,14 @@ class _PlaceInfoState extends State<PlaceInfo> {
void getTime() {
final weatherCard = widget.weatherCard;
timeNow =
weatherController.getTime(weatherCard.time!, weatherCard.timezone!);
dayNow =
weatherController.getDay(weatherCard.timeDaily!, weatherCard.timezone!);
timeNow = weatherController.getTime(
weatherCard.time!,
weatherCard.timezone!,
);
dayNow = weatherController.getDay(
weatherCard.timeDaily!,
weatherCard.timezone!,
);
Future.delayed(const Duration(milliseconds: 30), () {
itemScrollController.scrollTo(
index: timeNow,
@ -66,10 +67,7 @@ class _PlaceInfoState extends State<PlaceInfo> {
automaticallyImplyLeading: false,
leading: IconButton(
onPressed: () => Get.back(),
icon: const Icon(
IconsaxPlusLinear.arrow_left_3,
size: 20,
),
icon: const Icon(IconsaxPlusLinear.arrow_left_3, size: 20),
),
title: Text(
weatherCard.district!.isNotEmpty
@ -100,8 +98,10 @@ class _PlaceInfoState extends State<PlaceInfo> {
Card(
margin: const EdgeInsets.only(bottom: 15),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
child: SizedBox(
height: 135,
child: ScrollablePositionedList.separated(
@ -116,7 +116,8 @@ class _PlaceInfoState extends State<PlaceInfo> {
scrollDirection: Axis.horizontal,
itemScrollController: itemScrollController,
itemCount: weatherCard.time!.length,
itemBuilder: (ctx, i) => GestureDetector(
itemBuilder:
(ctx, i) => GestureDetector(
onTap: () {
timeNow = i;
dayNow = (i / 24).floor();
@ -129,8 +130,12 @@ class _PlaceInfoState extends State<PlaceInfo> {
vertical: 5,
),
decoration: BoxDecoration(
color: i == timeNow
? context.theme.colorScheme.secondaryContainer
color:
i == timeNow
? context
.theme
.colorScheme
.secondaryContainer
: Colors.transparent,
borderRadius: const BorderRadius.all(
Radius.circular(20),
@ -140,8 +145,10 @@ class _PlaceInfoState extends State<PlaceInfo> {
time: weatherCard.time![i],
weather: weatherCard.weathercode![i],
degree: weatherCard.temperature2M![i],
timeDay: weatherCard.sunrise![(i / 24).floor()],
timeNight: weatherCard.sunset![(i / 24).floor()],
timeDay:
weatherCard.sunrise![(i / 24).floor()],
timeNight:
weatherCard.sunset![(i / 24).floor()],
),
),
),
@ -175,10 +182,9 @@ class _PlaceInfoState extends State<PlaceInfo> {
),
DailyContainer(
weatherData: weatherCard,
onTap: () => Get.to(
() => DailyCardList(
weatherData: weatherCard,
),
onTap:
() => Get.to(
() => DailyCardList(weatherData: weatherCard),
transition: Transition.downToUp,
),
),

View file

@ -33,15 +33,13 @@ class _PlaceListState extends State<PlaceList> {
final textTheme = context.textTheme;
final titleMedium = textTheme.titleMedium;
return Obx(
() => weatherController.weatherCards.isEmpty
() =>
weatherController.weatherCards.isEmpty
? Center(
child: SingleChildScrollView(
child: Column(
children: [
Image.asset(
'assets/icons/City.png',
scale: 6,
),
Image.asset('assets/icons/City.png', scale: 6),
SizedBox(
width: Get.size.width * 0.8,
child: Text(
@ -71,9 +69,12 @@ class _PlaceListState extends State<PlaceList> {
),
controller: searchTasks,
margin: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
horizontal: 10,
vertical: 5,
),
onChanged: applyFilter,
iconButton: searchTasks.text.isNotEmpty
iconButton:
searchTasks.text.isNotEmpty
? IconButton(
onPressed: () {
searchTasks.clear();

View file

@ -9,11 +9,7 @@ import 'package:rain/app/ui/widgets/text_form.dart';
import 'package:rain/main.dart';
class CreatePlace extends StatefulWidget {
const CreatePlace({
super.key,
this.latitude,
this.longitude,
});
const CreatePlace({super.key, this.latitude, this.longitude});
final String? latitude;
final String? longitude;
@ -85,7 +81,8 @@ class _CreatePlaceState extends State<CreatePlace>
@override
Widget build(BuildContext context) {
const kTextFieldElevation = 4.0;
bool showButton = _controllerLon.text.isNotEmpty &&
bool showButton =
_controllerLon.text.isNotEmpty &&
_controllerLat.text.isNotEmpty &&
_controllerCity.text.isNotEmpty &&
_controllerDistrict.text.isNotEmpty;
@ -115,18 +112,21 @@ class _CreatePlaceState extends State<CreatePlace>
padding: const EdgeInsets.only(top: 14, bottom: 7),
child: Text(
'create'.tr,
style: context.textTheme.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
style: context.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
RawAutocomplete<Result>(
focusNode: _focusNode,
textEditingController: _controller,
fieldViewBuilder: (BuildContext context,
fieldViewBuilder: (
BuildContext context,
TextEditingController fieldTextEditingController,
FocusNode fieldFocusNode,
VoidCallback onFieldSubmitted) {
VoidCallback onFieldSubmitted,
) {
return MyTextForm(
elevation: kTextFieldElevation,
labelText: 'search'.tr,
@ -134,7 +134,10 @@ class _CreatePlaceState extends State<CreatePlace>
icon: const Icon(IconsaxPlusLinear.global_search),
controller: _controller,
margin: const EdgeInsets.only(
left: 10, right: 10, top: 10),
left: 10,
right: 10,
top: 10,
),
focusNode: _focusNode,
);
},
@ -142,16 +145,20 @@ class _CreatePlaceState extends State<CreatePlace>
if (textEditingValue.text.isEmpty) {
return const Iterable<Result>.empty();
}
return WeatherAPI()
.getCity(textEditingValue.text, locale);
return WeatherAPI().getCity(
textEditingValue.text,
locale,
);
},
onSelected: (Result selection) =>
fillController(selection),
displayStringForOption: (Result option) =>
'${option.name}, ${option.admin1}',
optionsViewBuilder: (BuildContext context,
onSelected:
(Result selection) => fillController(selection),
displayStringForOption:
(Result option) => '${option.name}, ${option.admin1}',
optionsViewBuilder: (
BuildContext context,
AutocompleteOnSelected<Result> onSelected,
Iterable<Result> options) {
Iterable<Result> options,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Align(
@ -164,8 +171,9 @@ class _CreatePlaceState extends State<CreatePlace>
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final Result option =
options.elementAt(index);
final Result option = options.elementAt(
index,
);
return InkWell(
onTap: () => onSelected(option),
child: ListTile(
@ -189,8 +197,11 @@ class _CreatePlaceState extends State<CreatePlace>
type: TextInputType.number,
icon: const Icon(IconsaxPlusLinear.location),
onChanged: (value) => setState(() {}),
margin:
const EdgeInsets.only(left: 10, right: 10, top: 10),
margin: const EdgeInsets.only(
left: 10,
right: 10,
top: 10,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'validateValue'.tr;
@ -212,8 +223,11 @@ class _CreatePlaceState extends State<CreatePlace>
type: TextInputType.number,
icon: const Icon(IconsaxPlusLinear.location),
onChanged: (value) => setState(() {}),
margin:
const EdgeInsets.only(left: 10, right: 10, top: 10),
margin: const EdgeInsets.only(
left: 10,
right: 10,
top: 10,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'validateValue'.tr;
@ -235,8 +249,11 @@ class _CreatePlaceState extends State<CreatePlace>
type: TextInputType.name,
icon: const Icon(IconsaxPlusLinear.building_3),
onChanged: (value) => setState(() {}),
margin:
const EdgeInsets.only(left: 10, right: 10, top: 10),
margin: const EdgeInsets.only(
left: 10,
right: 10,
top: 10,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'validateName'.tr;
@ -251,8 +268,11 @@ class _CreatePlaceState extends State<CreatePlace>
type: TextInputType.streetAddress,
icon: const Icon(IconsaxPlusLinear.global),
onChanged: (value) => setState(() {}),
margin:
const EdgeInsets.only(left: 10, right: 10, top: 10),
margin: const EdgeInsets.only(
left: 10,
right: 10,
top: 10,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'validateName'.tr;
@ -262,7 +282,9 @@ class _CreatePlaceState extends State<CreatePlace>
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
horizontal: 10,
vertical: 10,
),
child: SizeTransition(
sizeFactor: _animation,
axisAlignment: -1.0,
@ -291,10 +313,7 @@ class _CreatePlaceState extends State<CreatePlace>
],
),
),
if (isLoading)
const Center(
child: CircularProgressIndicator(),
),
if (isLoading) const Center(child: CircularProgressIndicator()),
],
),
),

View file

@ -54,10 +54,15 @@ class _PlaceCardState extends State<PlaceCard> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
statusData.getDegree(widget.degree[weatherController
.getTime(widget.time, widget.timezone)]
statusData.getDegree(
widget
.degree[weatherController.getTime(
widget.time,
widget.timezone,
)]
.round()
.toInt()),
.toInt(),
),
style: context.textTheme.titleLarge?.copyWith(
fontSize: 22,
fontWeight: FontWeight.w600,
@ -65,8 +70,12 @@ class _PlaceCardState extends State<PlaceCard> {
),
const Gap(7),
Text(
statusWeather.getText(widget.weather[weatherController
.getTime(widget.time, widget.timezone)]),
statusWeather.getText(
widget.weather[weatherController.getTime(
widget.time,
widget.timezone,
)],
),
style: context.textTheme.titleMedium?.copyWith(
color: Colors.grey,
fontWeight: FontWeight.w400,
@ -107,14 +116,23 @@ class _PlaceCardState extends State<PlaceCard> {
const Gap(5),
Image.asset(
statusWeather.getImageNow(
widget.weather[
weatherController.getTime(widget.time, widget.timezone)],
widget.time[
weatherController.getTime(widget.time, widget.timezone)],
widget.weather[weatherController.getTime(
widget.time,
widget.timezone,
)],
widget.time[weatherController.getTime(
widget.time,
widget.timezone,
)],
widget.timeDay[weatherController.getDay(
widget.timeDaily, widget.timezone)],
widget.timeDaily,
widget.timezone,
)],
widget.timeNight[weatherController.getDay(
widget.timeDaily, widget.timezone)]),
widget.timeDaily,
widget.timezone,
)],
),
scale: 6.5,
),
],

View file

@ -6,10 +6,7 @@ import 'package:rain/app/ui/places/view/place_info.dart';
import 'package:rain/app/ui/places/widgets/place_card.dart';
class PlaceCardList extends StatefulWidget {
const PlaceCardList({
super.key,
required this.searchCity,
});
const PlaceCardList({super.key, required this.searchCity});
final String searchCity;
@override
@ -24,15 +21,21 @@ class _PlaceCardListState extends State<PlaceCardList> {
final textTheme = context.textTheme;
final titleMedium = textTheme.titleMedium;
var weatherCards = weatherController.weatherCards
.where((weatherCard) => (widget.searchCity.isEmpty ||
weatherCard.city!.toLowerCase().contains(widget.searchCity)))
var weatherCards =
weatherController.weatherCards
.where(
(weatherCard) =>
(widget.searchCity.isEmpty ||
weatherCard.city!.toLowerCase().contains(
widget.searchCity,
)),
)
.toList()
.obs;
return ReorderableListView(
onReorder: (oldIndex, newIndex) =>
weatherController.reorder(oldIndex, newIndex),
onReorder:
(oldIndex, newIndex) => weatherController.reorder(oldIndex, newIndex),
children: [
...weatherCards.map(
(weatherCardList) => Dismissible(
@ -42,10 +45,7 @@ class _PlaceCardListState extends State<PlaceCardList> {
alignment: Alignment.centerRight,
child: const Padding(
padding: EdgeInsets.only(right: 15),
child: Icon(
IconsaxPlusLinear.trash_square,
color: Colors.red,
),
child: Icon(IconsaxPlusLinear.trash_square, color: Colors.red),
),
),
confirmDismiss: (DismissDirection direction) async {
@ -75,9 +75,7 @@ class _PlaceCardListState extends State<PlaceCardList> {
onPressed: () => Get.back(result: true),
child: Text(
'delete'.tr,
style: titleMedium?.copyWith(
color: Colors.red,
),
style: titleMedium?.copyWith(color: Colors.red),
),
),
],
@ -89,10 +87,9 @@ class _PlaceCardListState extends State<PlaceCardList> {
await weatherController.deleteCardWeather(weatherCardList);
},
child: GestureDetector(
onTap: () => Get.to(
() => PlaceInfo(
weatherCard: weatherCardList,
),
onTap:
() => Get.to(
() => PlaceInfo(weatherCard: weatherCardList),
transition: Transition.downToUp,
),
child: PlaceCard(

View file

@ -41,9 +41,7 @@ class SettingCard extends StatelessWidget {
elevation: elevation ?? 1,
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
onTap: onPressed,
leading: icon,
title: Text(
@ -51,13 +49,11 @@ class SettingCard extends StatelessWidget {
style: context.textTheme.titleMedium,
overflow: TextOverflow.visible,
),
trailing: switcher
trailing:
switcher
? Transform.scale(
scale: 0.8,
child: Switch(
value: value!,
onChanged: onChange,
),
child: Switch(value: value!, onChanged: onChange),
)
: dropdown
? DropdownButton<String>(
@ -70,8 +66,10 @@ class SettingCard extends StatelessWidget {
borderRadius: const BorderRadius.all(Radius.circular(15)),
underline: Container(),
value: dropdownName,
items: dropdownList!
.map<DropdownMenuItem<String>>((String value) {
items:
dropdownList!.map<DropdownMenuItem<String>>((
String value,
) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
@ -84,17 +82,11 @@ class SettingCard extends StatelessWidget {
? Wrap(
children: [
infoWidget!,
const Icon(
IconsaxPlusLinear.arrow_right_3,
size: 18,
),
const Icon(IconsaxPlusLinear.arrow_right_3, size: 18),
],
)
: infoWidget!
: const Icon(
IconsaxPlusLinear.arrow_right_3,
size: 18,
),
: const Icon(IconsaxPlusLinear.arrow_right_3, size: 18),
),
);
}

View file

@ -19,13 +19,11 @@ class MyTextButton extends StatelessWidget {
style: ButtonStyle(
shadowColor: const WidgetStatePropertyAll(Colors.transparent),
backgroundColor: WidgetStatePropertyAll(
context.theme.colorScheme.secondaryContainer.withAlpha(80)),
context.theme.colorScheme.secondaryContainer.withAlpha(80),
),
),
onPressed: onPressed,
child: Text(
buttonName,
style: context.textTheme.titleMedium,
),
child: Text(buttonName, style: context.textTheme.titleMedium),
),
);
}

View file

@ -3,11 +3,7 @@ import 'package:get/get.dart';
import 'package:shimmer/shimmer.dart';
class MyShimmer extends StatelessWidget {
const MyShimmer({
super.key,
required this.hight,
this.edgeInsetsMargin,
});
const MyShimmer({super.key, required this.hight, this.edgeInsetsMargin});
final double hight;
final EdgeInsets? edgeInsetsMargin;
@ -16,12 +12,7 @@ class MyShimmer extends StatelessWidget {
return Shimmer.fromColors(
baseColor: context.theme.cardColor,
highlightColor: context.theme.primaryColor,
child: Card(
margin: edgeInsetsMargin,
child: SizedBox(
height: hight,
),
),
child: Card(margin: edgeInsetsMargin, child: SizedBox(height: hight)),
);
}
}

View file

@ -50,8 +50,9 @@ class _DailyCardState extends State<DailyCard> {
),
const Gap(5),
Text(
DateFormat.MMMMEEEEd(locale.languageCode)
.format(widget.timeDaily),
DateFormat.MMMMEEEEd(
locale.languageCode,
).format(widget.timeDaily),
style: context.textTheme.titleMedium?.copyWith(
color: Colors.grey,
fontWeight: FontWeight.w400,

View file

@ -64,16 +64,14 @@ class _DailyCardInfoState extends State<DailyCardInfo> {
onPressed: () {
Get.back();
},
icon: const Icon(
IconsaxPlusLinear.arrow_left_3,
size: 20,
),
icon: const Icon(IconsaxPlusLinear.arrow_left_3, size: 20),
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
title: Text(
DateFormat.MMMMEEEEd(locale.languageCode)
.format(timeDaily[pageIndex]),
DateFormat.MMMMEEEEd(
locale.languageCode,
).format(timeDaily[pageIndex]),
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
fontSize: 18,
@ -121,10 +119,11 @@ class _DailyCardInfoState extends State<DailyCardInfo> {
Now(
weather:
weatherData.weathercode![startIndex + hourOfDay],
degree: weatherData
.temperature2M![startIndex + hourOfDay],
feels: weatherData
.apparentTemperature![startIndex + hourOfDay]!,
degree:
weatherData.temperature2M![startIndex + hourOfDay],
feels:
weatherData.apparentTemperature![startIndex +
hourOfDay]!,
time: weatherData.time![startIndex + hourOfDay],
timeDay: sunrise,
timeNight: sunset,
@ -135,12 +134,16 @@ class _DailyCardInfoState extends State<DailyCardInfo> {
margin: const EdgeInsets.only(bottom: 15),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
horizontal: 10,
vertical: 5,
),
child: SizedBox(
height: 135,
child: ScrollablePositionedList.separated(
separatorBuilder:
(BuildContext context, int index) {
separatorBuilder: (
BuildContext context,
int index,
) {
return const VerticalDivider(
width: 10,
indent: 40,
@ -158,14 +161,18 @@ class _DailyCardInfoState extends State<DailyCardInfo> {
},
child: Container(
margin: const EdgeInsets.symmetric(
vertical: 5),
vertical: 5,
),
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 5,
),
decoration: BoxDecoration(
color: i == hourOfDay
? context.theme.colorScheme
color:
i == hourOfDay
? context
.theme
.colorScheme
.secondaryContainer
: Colors.transparent,
borderRadius: const BorderRadius.all(
@ -174,9 +181,10 @@ class _DailyCardInfoState extends State<DailyCardInfo> {
),
child: Hourly(
time: weatherData.time![hourlyIndex],
weather: weatherData
.weathercode![hourlyIndex],
degree: weatherData
weather:
weatherData.weathercode![hourlyIndex],
degree:
weatherData
.temperature2M![hourlyIndex],
timeDay: sunrise,
timeNight: sunset,
@ -188,27 +196,28 @@ class _DailyCardInfoState extends State<DailyCardInfo> {
),
),
),
SunsetSunrise(
timeSunrise: sunrise,
timeSunset: sunset,
),
SunsetSunrise(timeSunrise: sunrise, timeSunset: sunset),
DescContainer(
humidity: weatherData
.relativehumidity2M?[startIndex + hourOfDay],
wind:
weatherData.windspeed10M?[startIndex + hourOfDay],
humidity:
weatherData.relativehumidity2M?[startIndex +
hourOfDay],
wind: weatherData.windspeed10M?[startIndex + hourOfDay],
visibility:
weatherData.visibility?[startIndex + hourOfDay],
feels: weatherData
.apparentTemperature?[startIndex + hourOfDay],
evaporation: weatherData
.evapotranspiration?[startIndex + hourOfDay],
precipitation: weatherData
.precipitation?[startIndex + hourOfDay],
direction: weatherData
.winddirection10M?[startIndex + hourOfDay],
pressure: weatherData
.surfacePressure?[startIndex + hourOfDay],
feels:
weatherData.apparentTemperature?[startIndex +
hourOfDay],
evaporation:
weatherData.evapotranspiration?[startIndex +
hourOfDay],
precipitation:
weatherData.precipitation?[startIndex + hourOfDay],
direction:
weatherData.winddirection10M?[startIndex +
hourOfDay],
pressure:
weatherData.surfacePressure?[startIndex +
hourOfDay],
rain: weatherData.rain?[startIndex + hourOfDay],
cloudcover:
weatherData.cloudcover?[startIndex + hourOfDay],
@ -218,10 +227,11 @@ class _DailyCardInfoState extends State<DailyCardInfo> {
dewpoint2M:
weatherData.dewpoint2M?[startIndex + hourOfDay],
precipitationProbability:
weatherData.precipitationProbability?[
startIndex + hourOfDay],
shortwaveRadiation: weatherData
.shortwaveRadiation?[startIndex + hourOfDay],
weatherData.precipitationProbability?[startIndex +
hourOfDay],
shortwaveRadiation:
weatherData.shortwaveRadiation?[startIndex +
hourOfDay],
initiallyExpanded: true,
title: 'hourlyVariables'.tr,
),

View file

@ -6,10 +6,7 @@ import 'package:rain/app/ui/widgets/weather/daily/daily_card_info.dart';
import 'package:rain/app/ui/widgets/weather/daily/daily_card.dart';
class DailyCardList extends StatefulWidget {
const DailyCardList({
super.key,
required this.weatherData,
});
const DailyCardList({super.key, required this.weatherData});
final WeatherCard weatherData;
@override
@ -31,10 +28,7 @@ class _DailyCardListState extends State<DailyCardList> {
onPressed: () {
Get.back();
},
icon: const Icon(
IconsaxPlusLinear.arrow_left_3,
size: 20,
),
icon: const Icon(IconsaxPlusLinear.arrow_left_3, size: 20),
splashColor: transparent,
highlightColor: transparent,
),
@ -49,12 +43,12 @@ class _DailyCardListState extends State<DailyCardList> {
body: SafeArea(
child: ListView.builder(
itemCount: timeDaily.length,
itemBuilder: (context, index) => GestureDetector(
onTap: () => Get.to(
() => DailyCardInfo(
weatherData: weatherData,
index: index,
),
itemBuilder:
(context, index) => GestureDetector(
onTap:
() => Get.to(
() =>
DailyCardInfo(weatherData: weatherData, index: index),
transition: Transition.downToUp,
),
child: DailyCard(

View file

@ -29,9 +29,7 @@ class _DailyContainerState extends State<DailyContainer> {
@override
Widget build(BuildContext context) {
final splashColor = context.theme.colorScheme.primary.withOpacity(0.4);
const inkWellBorderRadius = BorderRadius.all(
Radius.circular(16),
);
const inkWellBorderRadius = BorderRadius.all(Radius.circular(16));
final weatherData = widget.weatherData;
final weatherCodeDaily = weatherData.weathercodeDaily ?? [];
@ -52,7 +50,8 @@ class _DailyContainerState extends State<DailyContainer> {
return InkWell(
splashColor: splashColor,
borderRadius: inkWellBorderRadius,
onTap: () => Get.to(
onTap:
() => Get.to(
() => DailyCardInfo(
weatherData: weatherData,
index: index,
@ -66,8 +65,9 @@ class _DailyContainerState extends State<DailyContainer> {
children: [
Expanded(
child: Text(
DateFormat.EEEE(locale.languageCode)
.format((weatherData.timeDaily ?? [])[index]),
DateFormat.EEEE(
locale.languageCode,
).format((weatherData.timeDaily ?? [])[index]),
style: labelLarge,
overflow: TextOverflow.ellipsis,
),
@ -77,15 +77,17 @@ class _DailyContainerState extends State<DailyContainer> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
statusWeather
.getImage7Day(weatherCodeDaily[index]),
statusWeather.getImage7Day(
weatherCodeDaily[index],
),
scale: 3,
),
const Gap(5),
Expanded(
child: Text(
statusWeather
.getText(weatherCodeDaily[index]),
statusWeather.getText(
weatherCodeDaily[index],
),
style: labelLarge,
overflow: TextOverflow.ellipsis,
),
@ -100,17 +102,16 @@ class _DailyContainerState extends State<DailyContainer> {
Text(
statusData.getDegree(
(weatherData.temperature2MMin ?? [])[index]
?.round()),
style: labelLarge,
),
Text(
' / ',
?.round(),
),
style: labelLarge,
),
Text(' / ', style: labelLarge),
Text(
statusData.getDegree(
(weatherData.temperature2MMax ?? [])[index]
?.round()),
?.round(),
),
style: labelLarge,
),
],

View file

@ -35,14 +35,12 @@ class _DescWeatherState extends State<DescWeather> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
widget.imageName,
scale: 20,
),
Image.asset(widget.imageName, scale: 20),
const Gap(5),
Text(
widget.value,
style: textTheme.labelLarge,
overflow: TextOverflow.ellipsis,
),
Expanded(
child: Text(

View file

@ -107,10 +107,7 @@ class _DescContainerState extends State<DescContainer> {
margin: const EdgeInsets.only(bottom: 15),
child: ExpansionTile(
shape: const Border(),
title: Text(
title,
style: context.textTheme.labelLarge,
),
title: Text(title, style: context.textTheme.labelLarge),
initiallyExpanded: initiallyExpanded,
children: [
Padding(
@ -123,16 +120,18 @@ class _DescContainerState extends State<DescContainer> {
? Container()
: DescWeather(
imageName: 'assets/images/cold.png',
value: statusData
.getDegree(apparentTemperatureMin.round()),
value: statusData.getDegree(
apparentTemperatureMin.round(),
),
desc: 'apparentTemperatureMin'.tr,
),
apparentTemperatureMax == null
? Container()
: DescWeather(
imageName: 'assets/images/hot.png',
value: statusData
.getDegree(apparentTemperatureMax.round()),
value: statusData.getDegree(
apparentTemperatureMax.round(),
),
desc: 'apparentTemperatureMax'.tr,
),
uvIndexMax == null
@ -168,8 +167,7 @@ class _DescContainerState extends State<DescContainer> {
precipitationProbabilityMax == null
? Container()
: DescWeather(
imageName:
'assets/images/precipitation_probability.png',
imageName: 'assets/images/precipitation_probability.png',
value: '$precipitationProbabilityMax%',
desc: 'precipitationProbability'.tr,
),
@ -254,8 +252,7 @@ class _DescContainerState extends State<DescContainer> {
precipitationProbability == null
? Container()
: DescWeather(
imageName:
'assets/images/precipitation_probability.png',
imageName: 'assets/images/precipitation_probability.png',
value: '$precipitationProbability%',
desc: 'precipitationProbability'.tr,
),

View file

@ -37,16 +37,13 @@ class _HourlyState extends State<Hourly> {
children: [
Column(
children: [
Text(statusData.getTimeFormat(time), style: textTheme.labelLarge),
Text(
statusData.getTimeFormat(time),
style: textTheme.labelLarge,
),
Text(
DateFormat('E', locale.languageCode)
.format(DateTime.tryParse(time)!),
style: textTheme.labelLarge?.copyWith(
color: Colors.grey,
),
DateFormat(
'E',
locale.languageCode,
).format(DateTime.tryParse(time)!),
style: textTheme.labelLarge?.copyWith(color: Colors.grey),
),
],
),
@ -61,9 +58,7 @@ class _HourlyState extends State<Hourly> {
),
Text(
statusData.getDegree(widget.degree.round()),
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
style: textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
),
],
);

View file

@ -45,8 +45,14 @@ class _NowState extends State<Now> {
children: [
const Gap(15),
Image(
image: AssetImage(statusWeather.getImageNow(widget.weather,
widget.time, widget.timeDay, widget.timeNight)),
image: AssetImage(
statusWeather.getImageNow(
widget.weather,
widget.time,
widget.timeDay,
widget.timeNight,
),
),
fit: BoxFit.fill,
height: 200,
),
@ -55,12 +61,8 @@ class _NowState extends State<Now> {
style: context.textTheme.displayLarge?.copyWith(
fontSize: 90,
fontWeight: FontWeight.w800,
shadows: const [
Shadow(
blurRadius: 15,
offset: Offset(5, 5),
)
]),
shadows: const [Shadow(blurRadius: 15, offset: Offset(5, 5))],
),
),
Text(
statusWeather.getText(widget.weather),
@ -68,9 +70,9 @@ class _NowState extends State<Now> {
),
const Gap(5),
Text(
DateFormat.MMMMEEEEd(locale.languageCode).format(
DateTime.parse(widget.time),
),
DateFormat.MMMMEEEEd(
locale.languageCode,
).format(DateTime.parse(widget.time)),
style: context.textTheme.labelLarge?.copyWith(
color: Colors.grey,
),
@ -82,7 +84,11 @@ class _NowState extends State<Now> {
margin: const EdgeInsets.only(bottom: 15),
child: Padding(
padding: const EdgeInsets.only(
top: 18, bottom: 18, left: 25, right: 15),
top: 18,
bottom: 18,
left: 25,
right: 15,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@ -91,9 +97,9 @@ class _NowState extends State<Now> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
DateFormat.MMMMEEEEd(locale.languageCode).format(
DateTime.parse(widget.time),
),
DateFormat.MMMMEEEEd(
locale.languageCode,
).format(DateTime.parse(widget.time)),
style: context.textTheme.labelLarge?.copyWith(
color: Colors.grey,
),
@ -101,24 +107,26 @@ class _NowState extends State<Now> {
const Gap(5),
Text(
statusWeather.getText(widget.weather),
style: context.textTheme.titleLarge
?.copyWith(fontSize: 20),
style: context.textTheme.titleLarge?.copyWith(
fontSize: 20,
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('feels'.tr,
style: context.textTheme.bodyMedium),
Text('feels'.tr, style: context.textTheme.bodyMedium),
Text('', style: context.textTheme.bodyMedium),
Text(statusData.getDegree(widget.feels.round()),
style: context.textTheme.bodyMedium),
Text(
statusData.getDegree(widget.feels.round()),
style: context.textTheme.bodyMedium,
),
],
),
const Gap(30),
Text(
statusData.getDegree(roundDegree
? widget.degree.round()
: widget.degree),
statusData.getDegree(
roundDegree ? widget.degree.round() : widget.degree,
),
style: context.textTheme.displayMedium?.copyWith(
fontWeight: FontWeight.w800,
),
@ -127,19 +135,29 @@ class _NowState extends State<Now> {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(statusData.getDegree((widget.tempMin.round())),
style: context.textTheme.labelLarge),
Text(
statusData.getDegree((widget.tempMin.round())),
style: context.textTheme.labelLarge,
),
Text(' / ', style: context.textTheme.labelLarge),
Text(statusData.getDegree((widget.tempMax.round())),
style: context.textTheme.labelLarge),
Text(
statusData.getDegree((widget.tempMax.round())),
style: context.textTheme.labelLarge,
),
],
),
],
),
),
Image(
image: AssetImage(statusWeather.getImageNow(widget.weather,
widget.time, widget.timeDay, widget.timeNight)),
image: AssetImage(
statusWeather.getImageNow(
widget.weather,
widget.time,
widget.timeDay,
widget.timeNight,
),
),
fit: BoxFit.fill,
height: 140,
),

View file

@ -63,14 +63,17 @@ class StatusData {
String getTimeFormat(String time) {
switch (settings.timeformat) {
case '12':
return DateFormat.jm(locale.languageCode)
.format(DateTime.tryParse(time)!);
return DateFormat.jm(
locale.languageCode,
).format(DateTime.tryParse(time)!);
case '24':
return DateFormat.Hm(locale.languageCode)
.format(DateTime.tryParse(time)!);
return DateFormat.Hm(
locale.languageCode,
).format(DateTime.tryParse(time)!);
default:
return DateFormat.Hm(locale.languageCode)
.format(DateTime.tryParse(time)!);
return DateFormat.Hm(
locale.languageCode,
).format(DateTime.tryParse(time)!);
}
}

View file

@ -4,15 +4,29 @@ const assetImageRoot = 'assets/images/';
class StatusWeather {
String getImageNow(
int weather, String time, String timeDay, String timeNight) {
int weather,
String time,
String timeDay,
String timeNight,
) {
final currentTime = DateTime.parse(time);
final day = DateTime.parse(timeDay);
final night = DateTime.parse(timeNight);
final dayTime =
DateTime(day.year, day.month, day.day, day.hour, day.minute);
final nightTime =
DateTime(night.year, night.month, night.day, night.hour, night.minute);
final dayTime = DateTime(
day.year,
day.month,
day.day,
day.hour,
day.minute,
);
final nightTime = DateTime(
night.year,
night.month,
night.day,
night.hour,
night.minute,
);
switch (weather) {
case 0:
@ -112,15 +126,29 @@ class StatusWeather {
}
String getImageToday(
int weather, String time, String timeDay, String timeNight) {
int weather,
String time,
String timeDay,
String timeNight,
) {
final currentTime = DateTime.parse(time);
final day = DateTime.parse(timeDay);
final night = DateTime.parse(timeNight);
final dayTime =
DateTime(day.year, day.month, day.day, day.hour, day.minute);
final nightTime =
DateTime(night.year, night.month, night.day, night.hour, night.minute);
final dayTime = DateTime(
day.year,
day.month,
day.day,
day.hour,
day.minute,
);
final nightTime = DateTime(
night.year,
night.month,
night.day,
night.hour,
night.minute,
);
switch (weather) {
case 0:
@ -274,15 +302,29 @@ class StatusWeather {
}
String getImageNotification(
int weather, String time, String timeDay, String timeNight) {
int weather,
String time,
String timeDay,
String timeNight,
) {
final currentTime = DateTime.parse(time);
final day = DateTime.parse(timeDay);
final night = DateTime.parse(timeNight);
final dayTime =
DateTime(day.year, day.month, day.day, day.hour, day.minute);
final nightTime =
DateTime(night.year, night.month, night.day, night.hour, night.minute);
final dayTime = DateTime(
day.year,
day.month,
day.day,
day.hour,
day.minute,
);
final nightTime = DateTime(
night.year,
night.month,
night.day,
night.hour,
night.minute,
);
switch (weather) {
case 0:

View file

@ -55,10 +55,7 @@ class _SunsetSunriseState extends State<SunsetSunrise> {
),
const Gap(5),
Flexible(
child: Image.asset(
'assets/images/sunrise.png',
scale: 10,
),
child: Image.asset('assets/images/sunrise.png', scale: 10),
),
],
),
@ -86,10 +83,7 @@ class _SunsetSunriseState extends State<SunsetSunrise> {
),
const Gap(5),
Flexible(
child: Image.asset(
'assets/images/sunset.png',
scale: 10,
),
child: Image.asset('assets/images/sunset.png', scale: 10),
),
],
),

View file

@ -10,9 +10,17 @@ extension HexColor on Color {
}
/// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`).
String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}'
'${alpha.toRadixString(16).padLeft(2, '0')}'
'${red.toRadixString(16).padLeft(2, '0')}'
'${green.toRadixString(16).padLeft(2, '0')}'
'${blue.toRadixString(16).padLeft(2, '0')}';
String toHex({bool leadingHashSign = true}) {
final argb = toARGB32(); // Get 32-bit integer representation
final a = (argb >> 24) & 0xFF;
final r = (argb >> 16) & 0xFF;
final g = (argb >> 8) & 0xFF;
final b = argb & 0xFF;
return '${leadingHashSign ? '#' : ''}'
'${a.toRadixString(16).padLeft(2, '0')}'
'${r.toRadixString(16).padLeft(2, '0')}'
'${g.toRadixString(16).padLeft(2, '0')}'
'${b.toRadixString(16).padLeft(2, '0')}';
}
}

View file

@ -23,8 +23,9 @@ class NotificationShow {
enableVibration: false,
largeIcon: FilePathAndroidBitmap(imagePath),
);
NotificationDetails notificationDetails =
NotificationDetails(android: androidNotificationDetails);
NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
var scheduledTime = tz.TZDateTime.from(date, tz.local);
flutterLocalNotificationsPlugin.zonedSchedule(

View file

@ -7,7 +7,8 @@ void showSnackBar({required String content, Function? onPressed}) {
globalKey.currentState?.showSnackBar(
SnackBar(
content: Text(content),
action: onPressed != null
action:
onPressed != null
? SnackBarAction(
label: 'settings'.tr,
onPressed: () {

View file

@ -33,7 +33,7 @@ final ValueNotifier<Future<bool>> isOnline = ValueNotifier(
InternetConnection().hasInternetAccess,
);
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
bool amoledTheme = false;
@ -47,36 +47,36 @@ String timeEnd = '21:00';
String widgetBackgroundColor = '';
String widgetTextColor = '';
final List appLanguages = [
{'name': 'বাংলা', 'locale': const Locale('bn', 'IN')},
{'name': 'Čeština', 'locale': const Locale('cs', 'CZ')},
{'name': 'Dansk', 'locale': const Locale('da', 'DK')},
{'name': 'Deutsch', 'locale': const Locale('de', 'DE')},
{'name': 'English', 'locale': const Locale('en', 'US')},
{'name': 'Español', 'locale': const Locale('es', 'ES')},
{'name': 'Français', 'locale': const Locale('fr', 'FR')},
// {'name': 'Gaeilge', 'locale': const Locale('ga', 'IE')},
{'name': 'हिन्दी', 'locale': const Locale('hi', 'IN')},
{'name': 'Magyar', 'locale': const Locale('hu', 'HU')},
{'name': 'Italiano', 'locale': const Locale('it', 'IT')},
{'name': '한국어', 'locale': const Locale('ko', 'KR')},
{'name': 'فارسی', 'locale': const Locale('fa', 'IR')},
{'name': 'ქართული', 'locale': const Locale('ka', 'GE')},
{'name': 'Nederlands', 'locale': const Locale('nl', 'NL')},
{'name': 'Polski', 'locale': const Locale('pl', 'PL')},
{'name': 'Português (Brasil)', 'locale': const Locale('pt', 'BR')},
{'name': 'Română', 'locale': const Locale('ro', 'RO')},
{'name': 'Русский', 'locale': const Locale('ru', 'RU')},
{'name': 'Slovenčina', 'locale': const Locale('sk', 'SK')},
{'name': 'Türkçe', 'locale': const Locale('tr', 'TR')},
{'name': 'اردو', 'locale': const Locale('ur', 'PK')},
{'name': '中文(简体)', 'locale': const Locale('zh', 'CN')},
{'name': '中文(繁體)', 'locale': const Locale('zh', 'TW')},
];
const String appGroupId = 'DARK NIGHT';
const String androidWidgetName = 'OreoWidget';
const List<Map<String, dynamic>> appLanguages = [
{'name': 'বাংলা', 'locale': Locale('bn', 'IN')},
{'name': 'Čeština', 'locale': Locale('cs', 'CZ')},
{'name': 'Dansk', 'locale': Locale('da', 'DK')},
{'name': 'Deutsch', 'locale': Locale('de', 'DE')},
{'name': 'English', 'locale': Locale('en', 'US')},
{'name': 'Español', 'locale': Locale('es', 'ES')},
{'name': 'Français', 'locale': Locale('fr', 'FR')},
// {'name': 'Gaeilge', 'locale': Locale('ga', 'IE')},
{'name': 'हिन्दी', 'locale': Locale('hi', 'IN')},
{'name': 'Magyar', 'locale': Locale('hu', 'HU')},
{'name': 'Italiano', 'locale': Locale('it', 'IT')},
{'name': '한국어', 'locale': Locale('ko', 'KR')},
{'name': 'فارسی', 'locale': Locale('fa', 'IR')},
{'name': 'ქართული', 'locale': Locale('ka', 'GE')},
{'name': 'Nederlands', 'locale': Locale('nl', 'NL')},
{'name': 'Polski', 'locale': Locale('pl', 'PL')},
{'name': 'Português (Brasil)', 'locale': Locale('pt', 'BR')},
{'name': 'Română', 'locale': Locale('ro', 'RO')},
{'name': 'Русский', 'locale': Locale('ru', 'RU')},
{'name': 'Slovenčina', 'locale': Locale('sk', 'SK')},
{'name': 'Türkçe', 'locale': Locale('tr', 'TR')},
{'name': 'اردو', 'locale': Locale('ur', 'PK')},
{'name': '中文(简体)', 'locale': Locale('zh', 'CN')},
{'name': '中文(繁體)', 'locale': Locale('zh', 'TW')},
];
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) {
@ -85,59 +85,40 @@ void callbackDispatcher() {
}
void main() async {
final String timeZoneName;
WidgetsFlutterBinding.ensureInitialized();
Connectivity().onConnectivityChanged.listen((
List<ConnectivityResult> result,
) {
result.contains(ConnectivityResult.none)
? isOnline.value = Future(() => false)
: isOnline.value = InternetConnection().hasInternetAccess;
});
DeviceFeature().init();
if (Platform.isAndroid) {
Workmanager().initialize(callbackDispatcher, isInDebugMode: kDebugMode);
await setOptimalDisplayMode();
}
timeZoneName = await FlutterTimezone.getLocalTimezone();
tz.initializeTimeZones();
tz.setLocalLocation(tz.getLocation(timeZoneName));
await isarInit();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
const DarwinInitializationSettings initializationSettingsDarwin =
DarwinInitializationSettings();
const LinuxInitializationSettings initializationSettingsLinux =
LinuxInitializationSettings(defaultActionName: 'Rain');
const InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsDarwin,
linux: initializationSettingsLinux,
);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
await _initializeApp();
runApp(const MyApp());
}
Future<void> setOptimalDisplayMode() async {
final List<DisplayMode> supported = await FlutterDisplayMode.supported;
final DisplayMode active = await FlutterDisplayMode.active;
final List<DisplayMode> sameResolution =
supported
.where(
(DisplayMode m) =>
m.width == active.width && m.height == active.height,
)
.toList()
..sort(
(DisplayMode a, DisplayMode b) =>
b.refreshRate.compareTo(a.refreshRate),
);
final DisplayMode mostOptimalMode =
sameResolution.isNotEmpty ? sameResolution.first : active;
await FlutterDisplayMode.setPreferredMode(mostOptimalMode);
Future<void> _initializeApp() async {
_setupConnectivityListener();
await _initializeTimeZone();
await _initializeIsar();
await _initializeNotifications();
if (Platform.isAndroid) {
await _setOptimalDisplayMode();
Workmanager().initialize(callbackDispatcher, isInDebugMode: kDebugMode);
HomeWidget.setAppGroupId(appGroupId);
}
DeviceFeature().init();
}
Future<void> isarInit() async {
void _setupConnectivityListener() {
Connectivity().onConnectivityChanged.listen((result) {
isOnline.value =
result.contains(ConnectivityResult.none)
? Future.value(false)
: InternetConnection().hasInternetAccess;
});
}
Future<void> _initializeTimeZone() async {
final timeZoneName = await FlutterTimezone.getLocalTimezone();
tz.initializeTimeZones();
tz.setLocalLocation(tz.getLocation(timeZoneName));
}
Future<void> _initializeIsar() async {
isar = await Isar.open([
SettingsSchema,
MainWeatherCacheSchema,
@ -159,6 +140,28 @@ Future<void> isarInit() async {
}
}
Future<void> _initializeNotifications() async {
const initializationSettings = InitializationSettings(
android: AndroidInitializationSettings('@mipmap/ic_launcher'),
iOS: DarwinInitializationSettings(),
linux: LinuxInitializationSettings(defaultActionName: 'Rain'),
);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
Future<void> _setOptimalDisplayMode() async {
final supported = await FlutterDisplayMode.supported;
final active = await FlutterDisplayMode.active;
final sameResolution =
supported
.where((m) => m.width == active.width && m.height == active.height)
.toList()
..sort((a, b) => b.refreshRate.compareTo(a.refreshRate));
final mostOptimalMode =
sameResolution.isNotEmpty ? sameResolution.first : active;
await FlutterDisplayMode.setPreferredMode(mostOptimalMode);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@ -177,30 +180,14 @@ class MyApp extends StatefulWidget {
}) async {
final state = context.findAncestorStateOfType<_MyAppState>()!;
if (newAmoledTheme != null) {
state.changeAmoledTheme(newAmoledTheme);
}
if (newMaterialColor != null) {
state.changeMarerialTheme(newMaterialColor);
}
if (newRoundDegree != null) {
state.changeRoundDegree(newRoundDegree);
}
if (newLargeElement != null) {
state.changeLargeElement(newLargeElement);
}
if (newLocale != null) {
state.changeLocale(newLocale);
}
if (newTimeRange != null) {
state.changeTimeRange(newTimeRange);
}
if (newTimeStart != null) {
state.changeTimeStart(newTimeStart);
}
if (newTimeEnd != null) {
state.changeTimeEnd(newTimeEnd);
}
if (newAmoledTheme != null) state.changeAmoledTheme(newAmoledTheme);
if (newMaterialColor != null) state.changeMarerialTheme(newMaterialColor);
if (newRoundDegree != null) state.changeRoundDegree(newRoundDegree);
if (newLargeElement != null) state.changeLargeElement(newLargeElement);
if (newLocale != null) state.changeLocale(newLocale);
if (newTimeRange != null) state.changeTimeRange(newTimeRange);
if (newTimeStart != null) state.changeTimeStart(newTimeStart);
if (newTimeEnd != null) state.changeTimeEnd(newTimeEnd);
if (newWidgetBackgroundColor != null) {
state.changeWidgetBackgroundColor(newWidgetBackgroundColor);
}
@ -216,68 +203,28 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> {
final themeController = Get.put(ThemeController());
void changeAmoledTheme(bool newAmoledTheme) {
setState(() {
amoledTheme = newAmoledTheme;
});
}
void changeMarerialTheme(bool newMaterialColor) {
setState(() {
materialColor = newMaterialColor;
});
}
void changeRoundDegree(bool newRoundDegree) {
setState(() {
roundDegree = newRoundDegree;
});
}
void changeLargeElement(bool newLargeElement) {
setState(() {
largeElement = newLargeElement;
});
}
void changeTimeRange(int newTimeRange) {
setState(() {
timeRange = newTimeRange;
});
}
void changeTimeStart(String newTimeStart) {
setState(() {
timeStart = newTimeStart;
});
}
void changeTimeEnd(String newTimeEnd) {
setState(() {
timeEnd = newTimeEnd;
});
}
void changeLocale(Locale newLocale) {
setState(() {
locale = newLocale;
});
}
void changeWidgetBackgroundColor(String newWidgetBackgroundColor) {
setState(() {
widgetBackgroundColor = newWidgetBackgroundColor;
});
}
void changeWidgetTextColor(String newWidgetTextColor) {
setState(() {
widgetTextColor = newWidgetTextColor;
});
}
void changeAmoledTheme(bool newAmoledTheme) =>
setState(() => amoledTheme = newAmoledTheme);
void changeMarerialTheme(bool newMaterialColor) =>
setState(() => materialColor = newMaterialColor);
void changeRoundDegree(bool newRoundDegree) =>
setState(() => roundDegree = newRoundDegree);
void changeLargeElement(bool newLargeElement) =>
setState(() => largeElement = newLargeElement);
void changeTimeRange(int newTimeRange) =>
setState(() => timeRange = newTimeRange);
void changeTimeStart(String newTimeStart) =>
setState(() => timeStart = newTimeStart);
void changeTimeEnd(String newTimeEnd) => setState(() => timeEnd = newTimeEnd);
void changeLocale(Locale newLocale) => setState(() => locale = newLocale);
void changeWidgetBackgroundColor(String newWidgetBackgroundColor) =>
setState(() => widgetBackgroundColor = newWidgetBackgroundColor);
void changeWidgetTextColor(String newWidgetTextColor) =>
setState(() => widgetTextColor = newWidgetTextColor);
@override
void initState() {
super.initState();
amoledTheme = settings.amoledTheme;
materialColor = settings.materialColor;
roundDegree = settings.roundDegree;
@ -291,10 +238,6 @@ class _MyAppState extends State<MyApp> {
timeEnd = settings.timeEnd ?? '21:00';
widgetBackgroundColor = settings.widgetBackgroundColor ?? '';
widgetTextColor = settings.widgetTextColor ?? '';
if (Platform.isAndroid) {
HomeWidget.setAppGroupId(appGroupId);
}
super.initState();
}
@override
@ -379,10 +322,10 @@ class _MyAppState extends State<MyApp> {
debugShowCheckedModeBanner: false,
home:
settings.onboard
? (locationCache.city == null) ||
(locationCache.district == null) ||
(locationCache.lat == null) ||
(locationCache.lon == null)
? (locationCache.city == null ||
locationCache.district == null ||
locationCache.lat == null ||
locationCache.lon == null)
? const SelectGeolocation(isStart: true)
: const HomePage()
: const OnBording(),

View file

@ -20,10 +20,14 @@ ColorScheme colorSchemeDark = ColorScheme.fromSeed(
);
ThemeData lightTheme(
Color? color, ColorScheme? colorScheme, bool edgeToEdgeAvailable) {
Color? color,
ColorScheme? colorScheme,
bool edgeToEdgeAvailable,
) {
return baseLigth.copyWith(
brightness: Brightness.light,
colorScheme: colorScheme
colorScheme:
colorScheme
?.copyWith(
brightness: Brightness.light,
surface: baseLigth.colorScheme.surface,
@ -54,9 +58,7 @@ ThemeData lightTheme(
color: color,
surfaceTintColor:
color == oledColor ? Colors.transparent : colorScheme?.surfaceTint,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
shadowColor: Colors.transparent,
),
bottomSheetTheme: baseLigth.bottomSheetTheme.copyWith(
@ -73,11 +75,9 @@ ThemeData lightTheme(
color == oledColor ? Colors.transparent : colorScheme?.surfaceTint,
),
inputDecorationTheme: baseLigth.inputDecorationTheme.copyWith(
labelStyle: WidgetStateTextStyle.resolveWith(
(Set<WidgetState> states) {
labelStyle: WidgetStateTextStyle.resolveWith((Set<WidgetState> states) {
return const TextStyle(fontSize: 14);
},
),
}),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
@ -87,10 +87,14 @@ ThemeData lightTheme(
}
ThemeData darkTheme(
Color? color, ColorScheme? colorScheme, bool edgeToEdgeAvailable) {
Color? color,
ColorScheme? colorScheme,
bool edgeToEdgeAvailable,
) {
return baseDark.copyWith(
brightness: Brightness.dark,
colorScheme: colorScheme
colorScheme:
colorScheme
?.copyWith(
brightness: Brightness.dark,
surface: baseDark.colorScheme.surface,
@ -121,9 +125,7 @@ ThemeData darkTheme(
color: color,
surfaceTintColor:
color == oledColor ? Colors.transparent : colorScheme?.surfaceTint,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
shadowColor: Colors.transparent,
),
bottomSheetTheme: baseDark.bottomSheetTheme.copyWith(
@ -140,11 +142,9 @@ ThemeData darkTheme(
color == oledColor ? Colors.transparent : colorScheme?.surfaceTint,
),
inputDecorationTheme: baseDark.inputDecorationTheme.copyWith(
labelStyle: WidgetStateTextStyle.resolveWith(
(Set<WidgetState> states) {
labelStyle: WidgetStateTextStyle.resolveWith((Set<WidgetState> states) {
return const TextStyle(fontSize: 14);
},
),
}),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,

View file

@ -4,7 +4,8 @@ import 'package:rain/app/data/db.dart';
import 'package:rain/main.dart';
class ThemeController extends GetxController {
ThemeMode get theme => settings.theme == 'system'
ThemeMode get theme =>
settings.theme == 'system'
? ThemeMode.system
: settings.theme == 'dark'
? ThemeMode.dark

View file

@ -44,8 +44,7 @@ class EsEs {
'hPa': 'hPa',
'settings': 'Ajustes',
'no_inter': 'Sin conexión a Internet',
'on_inter':
'Conéctate a Internet para obtener información meteorológica.',
'on_inter': 'Conéctate a Internet para obtener información meteorológica.',
'location': 'Ubicación',
'no_location':
'Activa la localización para obtener información meteorológica para tu ubicación actual.',
@ -136,8 +135,7 @@ class EsEs {
'map': 'Mapa',
'clearCacheStore': 'Borrar caché',
'deletedCacheStore': 'Borrando caché',
'deletedCacheStoreQuery':
'¿Estás seguro de que quieres borrar el caché?',
'deletedCacheStoreQuery': '¿Estás seguro de que quieres borrar el caché?',
'addWidget': 'Agregar widget',
'hideMap': 'Ocultar mapa',
};

View file

@ -60,8 +60,7 @@ class FrFr {
'district': 'District',
'noWeatherCard': 'Ajouter une ville',
'deletedCardWeather': 'Supprimer une ville',
'deletedCardWeatherQuery':
'Êtes-vous sûr de vouloir supprimer la ville ?',
'deletedCardWeatherQuery': 'Êtes-vous sûr de vouloir supprimer la ville ?',
'delete': 'Supprimer',
'cancel': 'Annuler',
'time': 'Heure locale',

View file

@ -44,8 +44,7 @@ class HuHu {
'hPa': 'hPa',
'settings': 'Beállítások',
'no_inter': 'Nincs internet',
'on_inter':
'Kapcsolja be az internetet az időjárási adatok lekéréséhez.',
'on_inter': 'Kapcsolja be az internetet az időjárási adatok lekéréséhez.',
'location': 'Hely',
'no_location':
'Engedélyezze a helyszolgáltatást az aktuális hely időjárásadatainak megszerzéséhez.',

View file

@ -44,8 +44,7 @@ class ItIt {
'hPa': 'hPa',
'settings': 'Imposta.',
'no_inter': 'Non c\'è connessione Internet',
'on_inter':
'Attiva la connessione Internet per avere dati meteorologici.',
'on_inter': 'Attiva la connessione Internet per avere dati meteorologici.',
'location': 'Posizione',
'no_location':
'Abilita il servizio di localizzazione per ottenere i dati meteo per la posizione corrente.',
@ -60,8 +59,7 @@ class ItIt {
'district': 'Regione',
'noWeatherCard': 'Aggiungi una città',
'deletedCardWeather': 'Rimozione della città',
'deletedCardWeatherQuery':
'Sei sicuro di voler rimuovere questa città?',
'deletedCardWeatherQuery': 'Sei sicuro di voler rimuovere questa città?',
'delete': 'Elimina',
'cancel': 'Annulla',
'time': 'Orario locale',

View file

@ -59,8 +59,7 @@ class KaGe {
'district': 'რაიონი',
'noWeatherCard': 'დაამატეთ ქალაქი',
'deletedCardWeather': 'ქალაქის წაშლა',
'deletedCardWeatherQuery':
'დარწმუნებული ხართ, რომ გსურთ ქალაქის წაშლა?',
'deletedCardWeatherQuery': 'დარწმუნებული ხართ, რომ გსურთ ქალაქის წაშლა?',
'delete': 'ამოღება',
'cancel': 'გაუქმება',
'time': 'დრო ქალაქში',
@ -135,8 +134,7 @@ class KaGe {
'map': 'რუკა',
'clearCacheStore': 'ქეშის გასუფთავება',
'deletedCacheStore': 'ქეშის გასუფთავება მიმდინარეობს',
'deletedCacheStoreQuery':
'დარწმუნებული ხართ, რომ გსურთ ქეშის გასუფთავება?',
'deletedCacheStoreQuery': 'დარწმუნებული ხართ, რომ გსურთ ქეშის გასუფთავება?',
'addWidget': 'ვიდჯეტის დამატება',
'hideMap': 'რუკის დამალვა',
};

View file

@ -44,8 +44,7 @@ class NlNl {
'hPa': 'hPa',
'settings': 'Instellingen.',
'no_inter': 'Geen Internet',
'on_inter':
'Schakel Internet in om meteorologische gegevens te ontvangen.',
'on_inter': 'Schakel Internet in om meteorologische gegevens te ontvangen.',
'location': 'Locatie',
'no_location':
'Schakel de locatiedienst in om weer gegevens voor de huidige locatie te ontvangen.',
@ -60,8 +59,7 @@ class NlNl {
'district': 'District',
'noWeatherCard': 'Voeg een stad toe',
'deletedCardWeather': 'Verwijder een city',
'deletedCardWeatherQuery':
'Weet je zeker dat je de stad wilt verwijderen?',
'deletedCardWeatherQuery': 'Weet je zeker dat je de stad wilt verwijderen?',
'delete': 'Verwijder',
'cancel': 'Annuleer',
'time': 'Tijd in de stad',

View file

@ -134,8 +134,7 @@ class PlPl {
'map': 'Mapa',
'clearCacheStore': 'Wyczyść pamięć podręczną',
'deletedCacheStore': 'Czyszczenie pamięci podręcznej',
'deletedCacheStoreQuery':
'Czy na pewno chcesz wyczyścić pamięć podręczną?',
'deletedCacheStoreQuery': 'Czy na pewno chcesz wyczyścić pamięć podręczną?',
'addWidget': 'Dodaj widget',
'hideMap': 'Ukryj mapę',
};

View file

@ -1385,5 +1385,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.7.0 <4.0.0"
dart: ">=3.7.2 <4.0.0"
flutter: ">=3.27.0"

View file

@ -6,7 +6,7 @@ publish_to: "none"
version: 1.3.8+41
environment:
sdk: ">=3.7.0 <4.0.0"
sdk: ">=3.7.2 <4.0.0"
dependencies:
flutter: