mirror of
https://github.com/darkmoonight/Rain.git
synced 2025-06-28 12:09:57 +00:00
Fix bugs
This commit is contained in:
parent
d169f6237f
commit
fb150421a6
62 changed files with 5006 additions and 4901 deletions
|
@ -62,5 +62,5 @@ This project is licensed under the [MIT License](./LICENSE).
|
|||
### 👨💻 Our Contributors
|
||||
|
||||
<a href='https://github.com/darkmoonight/Rain/graphs/contributors'>
|
||||
<img src='https://contrib.rocks/image?repo=darkmoonight/Rain' />
|
||||
<img src='https://contrib.rocks/image?repo=darkmoonight/Rain'/>
|
||||
</a>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))),
|
||||
);
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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()),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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)!);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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')}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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: () {
|
||||
|
|
275
lib/main.dart
275
lib/main.dart
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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.',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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': 'რუკის დამალვა',
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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ę',
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue