From 8ea9b154ba304670b49b3d069441842463a1573b Mon Sep 17 00:00:00 2001 From: AimanYosofi Date: Fri, 24 Oct 2025 18:23:40 +0300 Subject: [PATCH 1/7] add location and weather DTOs --- lib/data/weather/model/current_units_dto.dart | 46 +++++++++++++++ .../weather/model/current_weather_dto.dart | 56 +++++++++++++++++++ lib/data/weather/model/daily_weather_dto.dart | 43 ++++++++++++++ .../model/daily_weather_units_dto.dart | 31 ++++++++++ .../weather/model/hourly_weather_dto.dart | 33 +++++++++++ .../model/hourly_weather_units_dto.dart | 23 ++++++++ lib/data/weather/model/location_dto.dart | 31 ++++++++++ .../repository/location_repository.dart | 5 ++ .../repository/location_repository_impl.dart | 32 +++++++++++ 9 files changed, 300 insertions(+) create mode 100644 lib/data/weather/model/current_units_dto.dart create mode 100644 lib/data/weather/model/current_weather_dto.dart create mode 100644 lib/data/weather/model/daily_weather_dto.dart create mode 100644 lib/data/weather/model/daily_weather_units_dto.dart create mode 100644 lib/data/weather/model/hourly_weather_dto.dart create mode 100644 lib/data/weather/model/hourly_weather_units_dto.dart create mode 100644 lib/data/weather/model/location_dto.dart create mode 100644 lib/data/weather/repository/location_repository.dart create mode 100644 lib/data/weather/repository/location_repository_impl.dart diff --git a/lib/data/weather/model/current_units_dto.dart b/lib/data/weather/model/current_units_dto.dart new file mode 100644 index 0000000..6abd5f7 --- /dev/null +++ b/lib/data/weather/model/current_units_dto.dart @@ -0,0 +1,46 @@ +import 'package:json_annotation/json_annotation.dart'; + +class CurrentUnitsDto { + final String? apparentTemperatureUnit; + final String? rainUnit; + final String? relativeHumidityUnit; + final String? surfacePressureUnit; + final String? temperatureUnit; + final String? timeUnit; + final String? windSpeedUnit; + + CurrentUnitsDto({ + this.apparentTemperatureUnit, + this.rainUnit, + this.relativeHumidityUnit, + this.surfacePressureUnit, + this.temperatureUnit, + this.timeUnit, + this.windSpeedUnit, + }); + + factory CurrentUnitsDto.fromJson(Map json) { + return CurrentUnitsDto( + apparentTemperatureUnit: json['apparent_temperature'] as String?, + rainUnit: json['rain'] as String?, + relativeHumidityUnit: json['relative_humidity_2m'] as String?, + surfacePressureUnit: json['surface_pressure'] as String?, + temperatureUnit: json['temperature_2m'] as String?, + timeUnit: json['time'] as String?, + windSpeedUnit: json['wind_speed_10m'] as String?, + ); + } + + + Map toJson() { + return { + 'apparent_temperature': apparentTemperatureUnit, + 'rain': rainUnit, + 'relative_humidity_2m': relativeHumidityUnit, + 'surface_pressure': surfacePressureUnit, + 'temperature_2m': temperatureUnit, + 'time': timeUnit, + 'wind_speed_10m': windSpeedUnit, + }; + } +} diff --git a/lib/data/weather/model/current_weather_dto.dart b/lib/data/weather/model/current_weather_dto.dart new file mode 100644 index 0000000..86174fa --- /dev/null +++ b/lib/data/weather/model/current_weather_dto.dart @@ -0,0 +1,56 @@ +class CurrentWeatherDto { + final double? apparentTemperature; + final double? interval; + final double? rain; + final double? relativeHumidity; + final double? surfacePressure; + final double? temperature; + final String? time; + final int? weatherCode; + final double? windSpeed; + final int? isDay; + + CurrentWeatherDto({ + this.apparentTemperature, + this.interval, + this.rain, + this.relativeHumidity, + this.surfacePressure, + this.temperature, + this.time, + this.weatherCode, + this.windSpeed, + this.isDay, + }); + + factory CurrentWeatherDto.fromJson(Map json) { + return CurrentWeatherDto( + apparentTemperature: (json['apparent_temperature'] as num?)?.toDouble(), + interval: (json['interval'] as num?)?.toDouble(), + rain: (json['rain'] as num?)?.toDouble(), + relativeHumidity: (json['relative_humidity_2m'] as num?)?.toDouble(), + surfacePressure: (json['surface_pressure'] as num?)?.toDouble(), + temperature: (json['temperature_2m'] as num?)?.toDouble(), + time: json['time'] as String?, + weatherCode: json['weather_code'] as int?, + windSpeed: (json['wind_speed_10m'] as num?)?.toDouble(), + isDay: json['is_day'] as int?, + ); + } + + + Map toJson() { + return { + 'apparent_temperature': apparentTemperature, + 'interval': interval, + 'rain': rain, + 'relative_humidity_2m': relativeHumidity, + 'surface_pressure': surfacePressure, + 'temperature_2m': temperature, + 'time': time, + 'weather_code': weatherCode, + 'wind_speed_10m': windSpeed, + 'is_day': isDay, + }; + } +} diff --git a/lib/data/weather/model/daily_weather_dto.dart b/lib/data/weather/model/daily_weather_dto.dart new file mode 100644 index 0000000..f272eaa --- /dev/null +++ b/lib/data/weather/model/daily_weather_dto.dart @@ -0,0 +1,43 @@ +class DailyWeatherDto { + final List? temperatureMax; + final List? temperatureMin; + final List? time; + final List? uvIndexMax; + final List? weatherCode; + + DailyWeatherDto({ + this.temperatureMax, + this.temperatureMin, + this.time, + this.uvIndexMax, + this.weatherCode, + }); + + factory DailyWeatherDto.fromJson(Map json) { + return DailyWeatherDto( + temperatureMax: (json['temperature_2m_max'] as List?) + ?.map((e) => (e as num).toDouble()) + .toList(), + temperatureMin: (json['temperature_2m_min'] as List?) + ?.map((e) => (e as num).toDouble()) + .toList(), + time: (json['time'] as List?)?.map((e) => e as String).toList(), + uvIndexMax: (json['uv_index_max'] as List?) + ?.map((e) => (e as num).toDouble()) + .toList(), + weatherCode: + (json['weather_code'] as List?)?.map((e) => e as int).toList(), + ); + } + + + Map toJson() { + return { + 'temperature_2m_max': temperatureMax, + 'temperature_2m_min': temperatureMin, + 'time': time, + 'uv_index_max': uvIndexMax, + 'weather_code': weatherCode, + }; + } +} diff --git a/lib/data/weather/model/daily_weather_units_dto.dart b/lib/data/weather/model/daily_weather_units_dto.dart new file mode 100644 index 0000000..fa5297d --- /dev/null +++ b/lib/data/weather/model/daily_weather_units_dto.dart @@ -0,0 +1,31 @@ +class DailyWeatherUnitsDto { + final String? temperatureMaxUnit; + final String? temperatureMinUnit; + final String? timeUnit; + final String? uvIndexUnit; + + DailyWeatherUnitsDto({ + this.temperatureMaxUnit, + this.temperatureMinUnit, + this.timeUnit, + this.uvIndexUnit, + }); + + factory DailyWeatherUnitsDto.fromJson(Map json) { + return DailyWeatherUnitsDto( + temperatureMaxUnit: json['temperature_2m_max'] as String?, + temperatureMinUnit: json['temperature_2m_min'] as String?, + timeUnit: json['time'] as String?, + uvIndexUnit: json['uv_index_max'] as String?, + ); + } + + Map toJson() { + return { + 'temperature_2m_max': temperatureMaxUnit, + 'temperature_2m_min': temperatureMinUnit, + 'time': timeUnit, + 'uv_index_max': uvIndexUnit, + }; + } +} diff --git a/lib/data/weather/model/hourly_weather_dto.dart b/lib/data/weather/model/hourly_weather_dto.dart new file mode 100644 index 0000000..195b052 --- /dev/null +++ b/lib/data/weather/model/hourly_weather_dto.dart @@ -0,0 +1,33 @@ +class HourlyWeatherDto { + final List? temperature; + final List? time; + final List? weatherCode; + + HourlyWeatherDto({ + this.temperature, + this.time, + this.weatherCode, + }); + + factory HourlyWeatherDto.fromJson(Map json) { + return HourlyWeatherDto( + temperature: (json['temperature_2m'] as List?) + ?.map((e) => (e as num).toDouble()) + .toList(), + time: (json['time'] as List?) + ?.map((e) => e as String) + .toList(), + weatherCode: (json['weather_code'] as List?) + ?.map((e) => e as int) + .toList(), + ); + } + + Map toJson() { + return { + 'temperature_2m': temperature, + 'time': time, + 'weather_code': weatherCode, + }; + } +} diff --git a/lib/data/weather/model/hourly_weather_units_dto.dart b/lib/data/weather/model/hourly_weather_units_dto.dart new file mode 100644 index 0000000..27a78e0 --- /dev/null +++ b/lib/data/weather/model/hourly_weather_units_dto.dart @@ -0,0 +1,23 @@ +class HourlyWeatherUnitsDto { + final String? temperatureUnit; + final String? timeUnit; + + HourlyWeatherUnitsDto({ + this.temperatureUnit, + this.timeUnit, + }); + + factory HourlyWeatherUnitsDto.fromJson(Map json) { + return HourlyWeatherUnitsDto( + temperatureUnit: json['temperature_2m'] as String?, + timeUnit: json['time'] as String?, + ); + } + + Map toJson() { + return { + 'temperature_2m': temperatureUnit, + 'time': timeUnit, + }; + } +} diff --git a/lib/data/weather/model/location_dto.dart b/lib/data/weather/model/location_dto.dart new file mode 100644 index 0000000..7a0101b --- /dev/null +++ b/lib/data/weather/model/location_dto.dart @@ -0,0 +1,31 @@ +class LocationDto { + final double latitude; + final double longitude; + final String country; + final String city; + + LocationDto({ + required this.latitude, + required this.longitude, + required this.country, + required this.city, + }); + + static Future fromJson(Map json) async { + return LocationDto( + latitude: (json['lat'])?.toDouble(), + longitude: (json['lon'])?.toDouble(), + country: (json['country']), + city: (json['city']), + ); + } + + Map toJson() { + return { + 'latitude': latitude, + 'longitude': longitude, + 'country': country, + 'city': city, + }; + } +} diff --git a/lib/data/weather/repository/location_repository.dart b/lib/data/weather/repository/location_repository.dart new file mode 100644 index 0000000..d7b0600 --- /dev/null +++ b/lib/data/weather/repository/location_repository.dart @@ -0,0 +1,5 @@ +import 'package:weather_app/data/weather/model/location_dto.dart'; + +abstract class LocationRepository { + Future getLocation(); +} \ No newline at end of file diff --git a/lib/data/weather/repository/location_repository_impl.dart b/lib/data/weather/repository/location_repository_impl.dart new file mode 100644 index 0000000..5a9fe5e --- /dev/null +++ b/lib/data/weather/repository/location_repository_impl.dart @@ -0,0 +1,32 @@ +import 'package:dio/dio.dart'; +import 'package:weather_app/data/weather/model/location_dto.dart'; +import 'package:weather_app/data/weather/repository/location_repository.dart'; + +class LocationRepositoryImpl extends LocationRepository { + final Dio dio; + + LocationRepositoryImpl(this.dio); + + @override + Future getLocation() async { + try { + final response = await dio.get( + "http://ip-api.com/json/", + queryParameters: { + 'fields': + 'status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,org,as,query', + }, + ); + + if (response.statusCode == 200 && response.data != null) { + return LocationDto.fromJson(response.data); + } else { + throw Exception( + 'Failed to fetch location. Status: ${response.statusCode}', + ); + } + } on DioException catch (e) { + throw Exception('Failed to fetch location: ${e.message}'); + } + } +} From e8bb3cc5271f5a810e79a557c93451bdb7784f02 Mon Sep 17 00:00:00 2001 From: AimanYosofi Date: Fri, 24 Oct 2025 18:24:17 +0300 Subject: [PATCH 2/7] add dio dependency --- pubspec.lock | 42 +++++++++++++++++++++++++++++++++++++++++- pubspec.yaml | 3 +++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index b78cc3c..0cca351 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -185,6 +185,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + dio: + dependency: "direct main" + description: + name: dio + sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 + url: "https://pub.dev" + source: hosted + version: "5.9.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" fake_async: dependency: transitive description: @@ -340,13 +356,21 @@ packages: source: hosted version: "1.0.5" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted version: "4.9.0" + json_serializable: + dependency: "direct main" + description: + name: json_serializable + sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" + url: "https://pub.dev" + source: hosted + version: "6.11.1" leak_tracker: dependency: transitive description: @@ -504,6 +528,22 @@ packages: description: flutter source: sdk version: "0.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" + url: "https://pub.dev" + source: hosted + version: "1.3.8" source_span: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a07196f..53fe906 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,9 @@ dependencies: cupertino_icons: ^1.0.8 flutter_svg: ^2.2.1 get_it: ^8.2.0 + dio: ^5.6.0 + json_serializable: 6.11.1 + json_annotation: 4.9.0 dev_dependencies: flutter_test: From c6664dc6eeb4dbf28e2f5536a7d1d5f26c523d73 Mon Sep 17 00:00:00 2001 From: AimanYosofi Date: Fri, 24 Oct 2025 18:24:32 +0300 Subject: [PATCH 3/7] add location and weather repository --- lib/data/weather/model/weather_dto.dart | 58 +++++++++++++++++++ .../repository/weather_repository.dart | 5 ++ .../repository/weather_repository_impl.dart | 39 +++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 lib/data/weather/model/weather_dto.dart create mode 100644 lib/data/weather/repository/weather_repository.dart create mode 100644 lib/data/weather/repository/weather_repository_impl.dart diff --git a/lib/data/weather/model/weather_dto.dart b/lib/data/weather/model/weather_dto.dart new file mode 100644 index 0000000..8d407b5 --- /dev/null +++ b/lib/data/weather/model/weather_dto.dart @@ -0,0 +1,58 @@ +import 'current_weather_dto.dart'; +import 'current_units_dto.dart'; +import 'daily_weather_dto.dart'; +import 'daily_weather_units_dto.dart'; +import 'hourly_weather_dto.dart'; +import 'hourly_weather_units_dto.dart'; + +class WeatherDto { + final CurrentWeatherDto? currentWeather; + final CurrentUnitsDto? currentUnits; + final DailyWeatherDto? dailyWeather; + final DailyWeatherUnitsDto? dailyWeatherUnits; + final HourlyWeatherDto? hourly; + final HourlyWeatherUnitsDto? hourlyWeatherUnits; + + WeatherDto({ + this.currentWeather, + this.currentUnits, + this.dailyWeather, + this.dailyWeatherUnits, + this.hourly, + this.hourlyWeatherUnits, + }); + + factory WeatherDto.fromJson(Map json) { + return WeatherDto( + currentWeather: json['current'] != null + ? CurrentWeatherDto.fromJson(json['current']) + : null, + currentUnits: json['current_units'] != null + ? CurrentUnitsDto.fromJson(json['current_units']) + : null, + dailyWeather: json['daily'] != null + ? DailyWeatherDto.fromJson(json['daily']) + : null, + dailyWeatherUnits: json['daily_units'] != null + ? DailyWeatherUnitsDto.fromJson(json['daily_units']) + : null, + hourly: json['hourly'] != null + ? HourlyWeatherDto.fromJson(json['hourly']) + : null, + hourlyWeatherUnits: json['hourly_units'] != null + ? HourlyWeatherUnitsDto.fromJson(json['hourly_units']) + : null, + ); + } + + Map toJson() { + return { + 'current': currentWeather?.toJson(), + 'current_units': currentUnits?.toJson(), + 'daily': dailyWeather?.toJson(), + 'daily_units': dailyWeatherUnits?.toJson(), + 'hourly': hourly?.toJson(), + 'hourly_units': hourlyWeatherUnits?.toJson(), + }; + } +} diff --git a/lib/data/weather/repository/weather_repository.dart b/lib/data/weather/repository/weather_repository.dart new file mode 100644 index 0000000..27d1c5c --- /dev/null +++ b/lib/data/weather/repository/weather_repository.dart @@ -0,0 +1,5 @@ +import 'package:weather_app/data/weather/model/weather_dto.dart'; + +abstract class WeatherRepository { + Future getWeather(double latitude, double longitude); +} \ No newline at end of file diff --git a/lib/data/weather/repository/weather_repository_impl.dart b/lib/data/weather/repository/weather_repository_impl.dart new file mode 100644 index 0000000..a161681 --- /dev/null +++ b/lib/data/weather/repository/weather_repository_impl.dart @@ -0,0 +1,39 @@ +import 'package:dio/dio.dart'; +import 'package:weather_app/data/weather/model/weather_dto.dart'; +import 'package:weather_app/data/weather/repository/weather_repository.dart'; + +const String openMeteoApi = 'https://api.open-meteo.com/v1/forecast'; + +class WeatherRepositoryImpl implements WeatherRepository { + final Dio dio; + + WeatherRepositoryImpl(this.dio); + + @override + Future getWeather(double latitude, double longitude) async { + try { + final response = await dio.get( + openMeteoApi, + queryParameters: { + 'latitude': latitude, + 'longitude': longitude, + 'daily': + 'temperature_2m_max,temperature_2m_min,uv_index_max,weather_code', + 'hourly': 'temperature_2m,weather_code', + 'current': + 'weather_code,relative_humidity_2m,wind_speed_10m,rain,surface_pressure,apparent_temperature,temperature_2m,is_day', + }, + ); + + if (response.statusCode == 200 && response.data != null) { + return WeatherDto.fromJson(response.data); + } else { + throw Exception( + 'Failed to fetch weather data. Status: ${response.statusCode}', + ); + } + } on DioException catch (e) { + throw Exception('Failed to fetch weather data: ${e.message}'); + } + } +} From 228d3e1cc40f332d84f642d111421f8c4a1b04e6 Mon Sep 17 00:00:00 2001 From: AimanYosofi Date: Fri, 24 Oct 2025 19:25:13 +0300 Subject: [PATCH 4/7] remove unused dependency --- lib/data/weather/model/current_units_dto.dart | 3 --- pubspec.yaml | 2 -- 2 files changed, 5 deletions(-) diff --git a/lib/data/weather/model/current_units_dto.dart b/lib/data/weather/model/current_units_dto.dart index 6abd5f7..1cf8bfe 100644 --- a/lib/data/weather/model/current_units_dto.dart +++ b/lib/data/weather/model/current_units_dto.dart @@ -1,5 +1,3 @@ -import 'package:json_annotation/json_annotation.dart'; - class CurrentUnitsDto { final String? apparentTemperatureUnit; final String? rainUnit; @@ -31,7 +29,6 @@ class CurrentUnitsDto { ); } - Map toJson() { return { 'apparent_temperature': apparentTemperatureUnit, diff --git a/pubspec.yaml b/pubspec.yaml index 53fe906..6bfaf32 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,8 +16,6 @@ dependencies: flutter_svg: ^2.2.1 get_it: ^8.2.0 dio: ^5.6.0 - json_serializable: 6.11.1 - json_annotation: 4.9.0 dev_dependencies: flutter_test: From 94426def82f1cc6f4238bdbe0811527f3955702d Mon Sep 17 00:00:00 2001 From: AimanYosofi Date: Sat, 25 Oct 2025 17:28:54 +0300 Subject: [PATCH 5/7] remove to json functions --- lib/data/weather/model/current_units_dto.dart | 12 ------------ lib/data/weather/model/current_weather_dto.dart | 16 ---------------- lib/data/weather/model/daily_weather_dto.dart | 11 ----------- .../weather/model/daily_weather_units_dto.dart | 9 --------- lib/data/weather/model/hourly_weather_dto.dart | 8 -------- .../weather/model/hourly_weather_units_dto.dart | 7 ------- lib/data/weather/model/location_dto.dart | 9 --------- lib/data/weather/model/weather_dto.dart | 11 ----------- 8 files changed, 83 deletions(-) diff --git a/lib/data/weather/model/current_units_dto.dart b/lib/data/weather/model/current_units_dto.dart index 1cf8bfe..054414a 100644 --- a/lib/data/weather/model/current_units_dto.dart +++ b/lib/data/weather/model/current_units_dto.dart @@ -28,16 +28,4 @@ class CurrentUnitsDto { windSpeedUnit: json['wind_speed_10m'] as String?, ); } - - Map toJson() { - return { - 'apparent_temperature': apparentTemperatureUnit, - 'rain': rainUnit, - 'relative_humidity_2m': relativeHumidityUnit, - 'surface_pressure': surfacePressureUnit, - 'temperature_2m': temperatureUnit, - 'time': timeUnit, - 'wind_speed_10m': windSpeedUnit, - }; - } } diff --git a/lib/data/weather/model/current_weather_dto.dart b/lib/data/weather/model/current_weather_dto.dart index 86174fa..836e724 100644 --- a/lib/data/weather/model/current_weather_dto.dart +++ b/lib/data/weather/model/current_weather_dto.dart @@ -37,20 +37,4 @@ class CurrentWeatherDto { isDay: json['is_day'] as int?, ); } - - - Map toJson() { - return { - 'apparent_temperature': apparentTemperature, - 'interval': interval, - 'rain': rain, - 'relative_humidity_2m': relativeHumidity, - 'surface_pressure': surfacePressure, - 'temperature_2m': temperature, - 'time': time, - 'weather_code': weatherCode, - 'wind_speed_10m': windSpeed, - 'is_day': isDay, - }; - } } diff --git a/lib/data/weather/model/daily_weather_dto.dart b/lib/data/weather/model/daily_weather_dto.dart index f272eaa..2fd843b 100644 --- a/lib/data/weather/model/daily_weather_dto.dart +++ b/lib/data/weather/model/daily_weather_dto.dart @@ -29,15 +29,4 @@ class DailyWeatherDto { (json['weather_code'] as List?)?.map((e) => e as int).toList(), ); } - - - Map toJson() { - return { - 'temperature_2m_max': temperatureMax, - 'temperature_2m_min': temperatureMin, - 'time': time, - 'uv_index_max': uvIndexMax, - 'weather_code': weatherCode, - }; - } } diff --git a/lib/data/weather/model/daily_weather_units_dto.dart b/lib/data/weather/model/daily_weather_units_dto.dart index fa5297d..9caac38 100644 --- a/lib/data/weather/model/daily_weather_units_dto.dart +++ b/lib/data/weather/model/daily_weather_units_dto.dart @@ -19,13 +19,4 @@ class DailyWeatherUnitsDto { uvIndexUnit: json['uv_index_max'] as String?, ); } - - Map toJson() { - return { - 'temperature_2m_max': temperatureMaxUnit, - 'temperature_2m_min': temperatureMinUnit, - 'time': timeUnit, - 'uv_index_max': uvIndexUnit, - }; - } } diff --git a/lib/data/weather/model/hourly_weather_dto.dart b/lib/data/weather/model/hourly_weather_dto.dart index 195b052..ebc79b4 100644 --- a/lib/data/weather/model/hourly_weather_dto.dart +++ b/lib/data/weather/model/hourly_weather_dto.dart @@ -22,12 +22,4 @@ class HourlyWeatherDto { .toList(), ); } - - Map toJson() { - return { - 'temperature_2m': temperature, - 'time': time, - 'weather_code': weatherCode, - }; - } } diff --git a/lib/data/weather/model/hourly_weather_units_dto.dart b/lib/data/weather/model/hourly_weather_units_dto.dart index 27a78e0..282f3ef 100644 --- a/lib/data/weather/model/hourly_weather_units_dto.dart +++ b/lib/data/weather/model/hourly_weather_units_dto.dart @@ -13,11 +13,4 @@ class HourlyWeatherUnitsDto { timeUnit: json['time'] as String?, ); } - - Map toJson() { - return { - 'temperature_2m': temperatureUnit, - 'time': timeUnit, - }; - } } diff --git a/lib/data/weather/model/location_dto.dart b/lib/data/weather/model/location_dto.dart index 7a0101b..c04e40c 100644 --- a/lib/data/weather/model/location_dto.dart +++ b/lib/data/weather/model/location_dto.dart @@ -19,13 +19,4 @@ class LocationDto { city: (json['city']), ); } - - Map toJson() { - return { - 'latitude': latitude, - 'longitude': longitude, - 'country': country, - 'city': city, - }; - } } diff --git a/lib/data/weather/model/weather_dto.dart b/lib/data/weather/model/weather_dto.dart index 8d407b5..adff982 100644 --- a/lib/data/weather/model/weather_dto.dart +++ b/lib/data/weather/model/weather_dto.dart @@ -44,15 +44,4 @@ class WeatherDto { : null, ); } - - Map toJson() { - return { - 'current': currentWeather?.toJson(), - 'current_units': currentUnits?.toJson(), - 'daily': dailyWeather?.toJson(), - 'daily_units': dailyWeatherUnits?.toJson(), - 'hourly': hourly?.toJson(), - 'hourly_units': hourlyWeatherUnits?.toJson(), - }; - } } From 02b891a5baf2704eb76167ff281dc804312e3608 Mon Sep 17 00:00:00 2001 From: AimanYosofi Date: Sat, 25 Oct 2025 17:55:07 +0300 Subject: [PATCH 6/7] add location mapper and convert repository to interface --- lib/data/weather/mapper/location_mapper.dart | 13 +++++++++++++ .../weather/repository/location_repository.dart | 5 ----- .../repository/location_repository_impl.dart | 11 ++++++++--- lib/domain/entity/location.dart | 13 ++++++++++--- lib/domain/repository/location_repository.dart | 6 +++--- lib/domain/repository/weather_repository.dart | 6 +++--- 6 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 lib/data/weather/mapper/location_mapper.dart delete mode 100644 lib/data/weather/repository/location_repository.dart diff --git a/lib/data/weather/mapper/location_mapper.dart b/lib/data/weather/mapper/location_mapper.dart new file mode 100644 index 0000000..299d908 --- /dev/null +++ b/lib/data/weather/mapper/location_mapper.dart @@ -0,0 +1,13 @@ +import 'package:weather_app/data/weather/model/location_dto.dart'; +import 'package:weather_app/domain/entity/location.dart'; + +class LocationMapper { + static Location toDomain(LocationDto dto) { + return Location( + latitude: dto.latitude, + longitude: dto.longitude, + country: dto.country, + city: dto.city, + ); + } +} diff --git a/lib/data/weather/repository/location_repository.dart b/lib/data/weather/repository/location_repository.dart deleted file mode 100644 index d7b0600..0000000 --- a/lib/data/weather/repository/location_repository.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:weather_app/data/weather/model/location_dto.dart'; - -abstract class LocationRepository { - Future getLocation(); -} \ No newline at end of file diff --git a/lib/data/weather/repository/location_repository_impl.dart b/lib/data/weather/repository/location_repository_impl.dart index 5a9fe5e..0c3ea73 100644 --- a/lib/data/weather/repository/location_repository_impl.dart +++ b/lib/data/weather/repository/location_repository_impl.dart @@ -1,6 +1,9 @@ import 'package:dio/dio.dart'; +import 'package:weather_app/data/weather/mapper/location_mapper.dart'; import 'package:weather_app/data/weather/model/location_dto.dart'; -import 'package:weather_app/data/weather/repository/location_repository.dart'; +import 'package:weather_app/domain/entity/location.dart'; + +import '../../../domain/repository/location_repository.dart'; class LocationRepositoryImpl extends LocationRepository { final Dio dio; @@ -8,7 +11,7 @@ class LocationRepositoryImpl extends LocationRepository { LocationRepositoryImpl(this.dio); @override - Future getLocation() async { + Future getCurrentLocation() async { try { final response = await dio.get( "http://ip-api.com/json/", @@ -19,7 +22,9 @@ class LocationRepositoryImpl extends LocationRepository { ); if (response.statusCode == 200 && response.data != null) { - return LocationDto.fromJson(response.data); + return LocationMapper.toDomain( + await LocationDto.fromJson(response.data), + ); } else { throw Exception( 'Failed to fetch location. Status: ${response.statusCode}', diff --git a/lib/domain/entity/location.dart b/lib/domain/entity/location.dart index 880324e..b7f61c1 100644 --- a/lib/domain/entity/location.dart +++ b/lib/domain/entity/location.dart @@ -1,8 +1,15 @@ import '../model/location_coordinate.dart'; class Location { - final LocationCoordinate coordinate; - final String cityName; + final double latitude; + final double longitude; + final String country; + final String city; - Location({required this.coordinate, required this.cityName}); + Location({ + required this.latitude, + required this.longitude, + required this.country, + required this.city, + }); } diff --git a/lib/domain/repository/location_repository.dart b/lib/domain/repository/location_repository.dart index f5cc7ee..23453dc 100644 --- a/lib/domain/repository/location_repository.dart +++ b/lib/domain/repository/location_repository.dart @@ -1,5 +1,5 @@ import 'package:weather_app/domain/entity/location.dart'; -abstract interface class LocationRepository { - Future getCurrentLocation(); -} \ No newline at end of file +abstract class LocationRepository { + Future getCurrentLocation(); +} diff --git a/lib/domain/repository/weather_repository.dart b/lib/domain/repository/weather_repository.dart index b397f54..3f2ab7e 100644 --- a/lib/domain/repository/weather_repository.dart +++ b/lib/domain/repository/weather_repository.dart @@ -1,6 +1,6 @@ import 'package:weather_app/domain/entity/weather.dart'; import '../model/location_coordinate.dart'; -abstract interface class WeatherRepository { - Future getWeatherForecast(LocationCoordinate locationCoordinate); -} \ No newline at end of file +abstract class WeatherRepository { + Future getWeatherForecast(LocationCoordinate locationCoordinate); +} From d3024b501996b12c72abdfb9c8e12f930f504b7e Mon Sep 17 00:00:00 2001 From: AimanYosofi Date: Sat, 25 Oct 2025 18:02:57 +0300 Subject: [PATCH 7/7] add weather mapper --- lib/data/weather/mapper/weather_mapper.dart | 220 ++++++++++++++++++ .../repository/weather_repository.dart | 5 - .../repository/weather_repository_impl.dart | 8 +- lib/domain/repository/weather_repository.dart | 2 +- 4 files changed, 226 insertions(+), 9 deletions(-) create mode 100644 lib/data/weather/mapper/weather_mapper.dart delete mode 100644 lib/data/weather/repository/weather_repository.dart diff --git a/lib/data/weather/mapper/weather_mapper.dart b/lib/data/weather/mapper/weather_mapper.dart new file mode 100644 index 0000000..9e4ea60 --- /dev/null +++ b/lib/data/weather/mapper/weather_mapper.dart @@ -0,0 +1,220 @@ +import 'package:weather_app/data/weather/model/weather_dto.dart'; +import 'package:weather_app/domain/entity/weather.dart'; +import 'package:weather_app/domain/model/temperature.dart'; +import 'package:weather_app/domain/model/current_day_weather_status.dart'; +import 'package:weather_app/domain/model/hourly_status.dart'; +import 'package:weather_app/domain/model/day_forecast.dart'; +import 'package:weather_app/domain/model/speed.dart'; +import 'package:weather_app/domain/model/atmospheric_pressure.dart'; + +class WeatherMapper { + static Weather toDomain(WeatherDto dto) { + return Weather( + currentTemperature: _mapCurrentTemperature(dto), + currentDayWeatherStatus: _mapCurrentDayWeatherStatus(dto), + hourlyStatus: _mapHourlyStatus(dto), + currentDayForecast: _mapCurrentDayForecast(dto), + nextDaysForecast: _mapNextDaysForecast(dto), + isDaytime: _mapIsDaytime(dto), + ); + } + + static Temperature _mapCurrentTemperature(WeatherDto dto) { + final temperature = dto.currentWeather?.temperature ?? 0.0; + final unit = _parseTemperatureUnit(dto.currentUnits?.temperatureUnit); + return Temperature(temperature: temperature, unit: unit); + } + + static TodayWeatherStatus _mapCurrentDayWeatherStatus(WeatherDto dto) { + final windSpeed = Speed( + speed: dto.currentWeather?.windSpeed ?? 0.0, + unit: _parseSpeedUnit(dto.currentUnits?.windSpeedUnit), + ); + + final feelsLike = Temperature( + temperature: dto.currentWeather?.apparentTemperature ?? 0.0, + unit: _parseTemperatureUnit(dto.currentUnits?.apparentTemperatureUnit), + ); + + final pressure = AtmosphericPressure( + pressure: dto.currentWeather?.surfacePressure ?? 0.0, + unit: _parsePressureUnit(dto.currentUnits?.surfacePressureUnit), + ); + + final uvIndex = dto.dailyWeather?.uvIndexMax?.isNotEmpty == true + ? dto.dailyWeather!.uvIndexMax!.first + : 0.0; + + return TodayWeatherStatus( + windSpeed: windSpeed, + humidity: dto.currentWeather?.relativeHumidity ?? 0.0, + rain: dto.currentWeather?.rain ?? 0.0, + uvIndex: uvIndex, + pressure: pressure, + feelsLike: feelsLike, + ); + } + + static List _mapHourlyStatus(WeatherDto dto) { + final hourly = dto.hourly; + if (hourly?.time == null || + hourly?.temperature == null || + hourly?.weatherCode == null) { + return []; + } + + final List hourlyStatuses = []; + final timeList = hourly!.time!; + final tempList = hourly.temperature!; + final codeList = hourly.weatherCode!; + + final maxLength = [ + timeList.length, + tempList.length, + codeList.length, + ].reduce((a, b) => a < b ? a : b); + + for (int i = 0; i < maxLength; i++) { + final timeString = timeList[i]; + final hour = DateTime.parse(timeString).hour; + + final temperature = Temperature( + temperature: tempList[i], + unit: _parseTemperatureUnit(dto.currentUnits?.temperatureUnit), + ); + + hourlyStatuses.add( + HourlyStatus( + hour: hour, + temperature: temperature, + weatherCode: codeList[i], + ), + ); + } + + return hourlyStatuses; + } + + static DayForecast _mapCurrentDayForecast(WeatherDto dto) { + final daily = dto.dailyWeather; + if (daily?.temperatureMin == null || + daily?.temperatureMax == null || + daily?.weatherCode == null) { + return DayForecast( + minTemperature: Temperature( + temperature: 0.0, + unit: TemperatureUnit.celecus, + ), + maxTemperature: Temperature( + temperature: 0.0, + unit: TemperatureUnit.celecus, + ), + weatherConditionCode: 0, + ); + } + + final tempUnit = _parseTemperatureUnit(dto.currentUnits?.temperatureUnit); + + return DayForecast( + minTemperature: Temperature( + temperature: daily!.temperatureMin!.first, + unit: tempUnit, + ), + maxTemperature: Temperature( + temperature: daily.temperatureMax!.first, + unit: tempUnit, + ), + weatherConditionCode: daily.weatherCode!.first, + ); + } + + static List _mapNextDaysForecast(WeatherDto dto) { + final daily = dto.dailyWeather; + if (daily?.temperatureMin == null || + daily?.temperatureMax == null || + daily?.weatherCode == null) { + return []; + } + + final List forecasts = []; + final minTempList = daily!.temperatureMin!; + final maxTempList = daily.temperatureMax!; + final codeList = daily.weatherCode!; + final tempUnit = _parseTemperatureUnit(dto.currentUnits?.temperatureUnit); + + final maxLength = [ + minTempList.length, + maxTempList.length, + codeList.length, + ].reduce((a, b) => a < b ? a : b); + + // Skip the first day (current day) and get the next days + for (int i = 1; i < maxLength; i++) { + forecasts.add( + DayForecast( + minTemperature: Temperature( + temperature: minTempList[i], + unit: tempUnit, + ), + maxTemperature: Temperature( + temperature: maxTempList[i], + unit: tempUnit, + ), + weatherConditionCode: codeList[i], + ), + ); + } + + return forecasts; + } + + static bool _mapIsDaytime(WeatherDto dto) { + return dto.currentWeather?.isDay == 1; + } + + static TemperatureUnit _parseTemperatureUnit(String? unit) { + switch (unit?.toLowerCase()) { + case '°f': + case 'fahrenheit': + return TemperatureUnit.fehrenhite; + case '°c': + case 'celsius': + default: + return TemperatureUnit.celecus; + } + } + + static SpeedUnit _parseSpeedUnit(String? unit) { + switch (unit?.toLowerCase()) { + case 'mph': + case 'mi/h': + return SpeedUnit.mph; + case 'km/h': + case 'kmph': + default: + return SpeedUnit.kmph; + } + } + + static AtmosphericPressureUnit _parsePressureUnit(String? unit) { + switch (unit?.toLowerCase()) { + case 'hpa': + return AtmosphericPressureUnit.hPa; + case 'kpa': + return AtmosphericPressureUnit.kPa; + case 'mbar': + return AtmosphericPressureUnit.mbar; + case 'atm': + return AtmosphericPressureUnit.atm; + case 'psi': + return AtmosphericPressureUnit.psi; + case 'mmhg': + return AtmosphericPressureUnit.mmHg; + case 'inhg': + return AtmosphericPressureUnit.inHg; + case 'pa': + default: + return AtmosphericPressureUnit.pa; + } + } +} diff --git a/lib/data/weather/repository/weather_repository.dart b/lib/data/weather/repository/weather_repository.dart deleted file mode 100644 index 27d1c5c..0000000 --- a/lib/data/weather/repository/weather_repository.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:weather_app/data/weather/model/weather_dto.dart'; - -abstract class WeatherRepository { - Future getWeather(double latitude, double longitude); -} \ No newline at end of file diff --git a/lib/data/weather/repository/weather_repository_impl.dart b/lib/data/weather/repository/weather_repository_impl.dart index a161681..447f328 100644 --- a/lib/data/weather/repository/weather_repository_impl.dart +++ b/lib/data/weather/repository/weather_repository_impl.dart @@ -1,6 +1,8 @@ import 'package:dio/dio.dart'; +import 'package:weather_app/data/weather/mapper/weather_mapper.dart'; import 'package:weather_app/data/weather/model/weather_dto.dart'; -import 'package:weather_app/data/weather/repository/weather_repository.dart'; +import 'package:weather_app/domain/entity/weather.dart'; +import 'package:weather_app/domain/repository/weather_repository.dart'; const String openMeteoApi = 'https://api.open-meteo.com/v1/forecast'; @@ -10,7 +12,7 @@ class WeatherRepositoryImpl implements WeatherRepository { WeatherRepositoryImpl(this.dio); @override - Future getWeather(double latitude, double longitude) async { + Future getWeatherForecast(double latitude, double longitude) async { try { final response = await dio.get( openMeteoApi, @@ -26,7 +28,7 @@ class WeatherRepositoryImpl implements WeatherRepository { ); if (response.statusCode == 200 && response.data != null) { - return WeatherDto.fromJson(response.data); + return WeatherMapper.toDomain(WeatherDto.fromJson(response.data)); } else { throw Exception( 'Failed to fetch weather data. Status: ${response.statusCode}', diff --git a/lib/domain/repository/weather_repository.dart b/lib/domain/repository/weather_repository.dart index 3f2ab7e..78d871c 100644 --- a/lib/domain/repository/weather_repository.dart +++ b/lib/domain/repository/weather_repository.dart @@ -2,5 +2,5 @@ import 'package:weather_app/domain/entity/weather.dart'; import '../model/location_coordinate.dart'; abstract class WeatherRepository { - Future getWeatherForecast(LocationCoordinate locationCoordinate); + Future getWeatherForecast(double latitude, double longitude); }