From 1dd7e1d8c89eef954fce3ad465a85e22df8a65bd Mon Sep 17 00:00:00 2001 From: hab Date: Sat, 23 Dec 2023 19:48:10 +0200 Subject: [PATCH 1/4] update connectivity state --- lib/app/app.dart | 3 ++- lib/app/bloc/network/network_cubit.dart | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/app/app.dart b/lib/app/app.dart index 47a538d..cd70cf7 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,5 +1,6 @@ import 'package:clima/app/bloc/network/network_cubit.dart'; import 'package:clima/app/bloc/theme/theme_cubit.dart'; +import 'package:clima/core/common/loading_widget.dart'; import 'package:clima/core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -32,7 +33,7 @@ class MyApp extends StatelessWidget { builder: (context, state) { if (state is ConnectedInitial) { return const Scaffold( - body: Center(child: CircularProgressIndicator()), + body: LoadingWidget(), ); } else if (state is ConnectedSuccess) { return const LandingScreen(); diff --git a/lib/app/bloc/network/network_cubit.dart b/lib/app/bloc/network/network_cubit.dart index 72a18a1..4c8d531 100644 --- a/lib/app/bloc/network/network_cubit.dart +++ b/lib/app/bloc/network/network_cubit.dart @@ -12,7 +12,7 @@ class NetworkCubit extends Cubit { late final StreamSubscription connectivityStreamSubscription; final Connectivity connectivity = Connectivity(); - NetworkCubit() : super(ConnectedFailure()) { + NetworkCubit() : super(ConnectedInitial()) { connectivityStreamSubscription = connectivity.onConnectivityChanged.listen((result) { print(result); From 9d3abb26a5df0f58a5e5183f4f988fde7a836765 Mon Sep 17 00:00:00 2001 From: hab Date: Tue, 26 Dec 2023 16:48:11 +0200 Subject: [PATCH 2/4] new api for more details --- lib/core/global/enums.dart | 45 ++++++++ lib/core/helper/converter_helper.dart | 42 +++++++ lib/core/services/get_it_service.dart | 8 +- .../cubit/daily_forecast_cubit.dart | 60 ---------- .../cubit/daily_forecast_state.dart | 22 ---- .../cubit/detailed_forecast_cubit.dart | 77 +++++++++++++ .../cubit/detailed_forecast_state.dart | 27 +++++ .../data/models/daily_forecast_model.dart | 30 +++-- .../data/models/weather_data.dart | 97 ++++++++++++++++ .../data/repo/daily_forecast_repo.dart | 9 -- .../data/repo/daily_forecast_repo_impl.dart | 35 ------ .../data/repo/detailed_forecast_repo.dart | 9 ++ .../repo/detailed_forecast_repo_impl.dart | 24 ++++ .../screens/daily_forecast_screen.dart | 12 +- .../screens/widgets/daily_weather_list.dart | 108 +++++++++--------- lib/features/home/cubit/home_cubit.dart | 23 ++-- lib/features/home/data/repo/home_repo.dart | 2 +- .../home/data/repo/home_repo_impl.dart | 2 +- .../cubit/hourly_forecast_cubit.dart | 63 ---------- .../cubit/hourly_forecast_state.dart | 22 ---- .../data/models/hourly_forecast_model.dart | 45 ++++---- .../screens/hourly_forecast_screen.dart | 15 +-- .../widgets/hourly_forecast_details.dart | 8 +- .../widgets/hourly_forecast_widget.dart | 2 +- lib/features/landing_page/landing_screen.dart | 21 ++-- pubspec.lock | 16 +++ pubspec.yaml | 2 + 27 files changed, 475 insertions(+), 351 deletions(-) delete mode 100644 lib/features/daily_forecast/cubit/daily_forecast_cubit.dart delete mode 100644 lib/features/daily_forecast/cubit/daily_forecast_state.dart create mode 100644 lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart create mode 100644 lib/features/daily_forecast/cubit/detailed_forecast_state.dart create mode 100644 lib/features/daily_forecast/data/models/weather_data.dart delete mode 100644 lib/features/daily_forecast/data/repo/daily_forecast_repo.dart delete mode 100644 lib/features/daily_forecast/data/repo/daily_forecast_repo_impl.dart create mode 100644 lib/features/daily_forecast/data/repo/detailed_forecast_repo.dart create mode 100644 lib/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart delete mode 100644 lib/features/hourly_forecast/cubit/hourly_forecast_cubit.dart delete mode 100644 lib/features/hourly_forecast/cubit/hourly_forecast_state.dart diff --git a/lib/core/global/enums.dart b/lib/core/global/enums.dart index 62b0c9b..497116a 100644 --- a/lib/core/global/enums.dart +++ b/lib/core/global/enums.dart @@ -36,3 +36,48 @@ extension MapWeatherState on String { } } } + +extension MapWeatherCode on int { + WeatherState mapToWeatherState() { + switch (this) { + case 0: + return WeatherState.Clear; + case 1: + case 2: + case 3: + case 45: + case 48: + return WeatherState.Clouds; + case 51: + case 53: + case 55: + case 56: + case 57: + return WeatherState.Rain; + case 61: + case 63: + case 65: + case 66: + case 67: + return WeatherState.Rain; + case 71: + case 73: + case 75: + case 77: + return WeatherState.Snow; + case 80: + case 81: + case 82: + case 85: + case 86: + return WeatherState.Rain; + case 95: + return WeatherState.Storm; + case 96: + case 99: + return WeatherState.Storm; + default: + return WeatherState.Unknown; + } + } +} diff --git a/lib/core/helper/converter_helper.dart b/lib/core/helper/converter_helper.dart index 4cb5e42..db963c2 100644 --- a/lib/core/helper/converter_helper.dart +++ b/lib/core/helper/converter_helper.dart @@ -1,3 +1,7 @@ +import 'package:clima/core/utils/utils.dart'; + +import '../global/enums.dart'; + class TemperatureConverter { static double kelvinToCelsius(double kelvin) { return kelvin - 273.15; @@ -56,3 +60,41 @@ class DateFormatter { return formattedTime; } } + +String getDayImage(WeatherState weatherState) { + switch (weatherState) { + case WeatherState.Storm: + return AppLottie.dailyStorm; + case WeatherState.Rain: + return AppLottie.dailyDayRain; + case WeatherState.Snow: + return AppLottie.dailyDaySnow; + case WeatherState.Wind: + return AppLottie.dailyWind; + case WeatherState.Clear: + return AppLottie.dailyDay; + case WeatherState.Clouds: + return AppLottie.dailyDayCloud; + default: + return 'unknown_image.png'; + } +} + +String getNightImage(WeatherState weatherState) { + switch (weatherState) { + case WeatherState.Storm: + return AppLottie.dailyStorm; + case WeatherState.Rain: + return AppLottie.dailyNightRain; + case WeatherState.Snow: + return AppLottie.dailyNightSnow; + case WeatherState.Wind: + return AppLottie.dailyWind; + case WeatherState.Clear: + return AppLottie.dailyNight; + case WeatherState.Clouds: + return AppLottie.dailyNightCloud; + default: + return 'unknown_image.png'; + } +} diff --git a/lib/core/services/get_it_service.dart b/lib/core/services/get_it_service.dart index 595eecd..0ec625c 100644 --- a/lib/core/services/get_it_service.dart +++ b/lib/core/services/get_it_service.dart @@ -1,6 +1,6 @@ import 'package:clima/core/services/api_service.dart'; -import 'package:clima/features/daily_forecast/data/repo/daily_forecast_repo.dart'; -import 'package:clima/features/daily_forecast/data/repo/daily_forecast_repo_impl.dart'; +import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; +import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart'; import 'package:clima/features/home/data/repo/home_repo.dart'; import 'package:clima/features/home/data/repo/home_repo_impl.dart'; import 'package:get_it/get_it.dart'; @@ -11,6 +11,6 @@ void setup() { getIt.registerLazySingleton(() => ApiService()); getIt.registerLazySingleton( () => HomeRepoImpl(getIt.get())); - getIt.registerLazySingleton( - () => DailyForecastRepoImpl(getIt.get())); + getIt.registerLazySingleton( + () => DetailedForecastRepoImpl()); } diff --git a/lib/features/daily_forecast/cubit/daily_forecast_cubit.dart b/lib/features/daily_forecast/cubit/daily_forecast_cubit.dart deleted file mode 100644 index fde629a..0000000 --- a/lib/features/daily_forecast/cubit/daily_forecast_cubit.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:clima/core/global/enums.dart'; -import 'package:clima/core/global/variables.dart'; -import 'package:clima/core/helper/location_helper.dart'; -import 'package:clima/features/daily_forecast/data/models/forecast_5_days_model.dart'; -import 'package:clima/features/daily_forecast/data/repo/daily_forecast_repo.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../../core/helper/functions.dart'; -import '../../home/data/model/weather_theme.dart'; -import '../data/models/daily_forecast_model.dart'; - -part 'daily_forecast_state.dart'; - -class DailyForecastCubit extends Cubit { - DailyForecastCubit(this._repository) : super(DailyForecastInitial()); - final DailyForecastRepository _repository; - fetchForecast5DaysData() async { - var result = await _repository.fetchForecast5Days( - Location.instance.position?.latitude, - Location.instance.position?.longitude); - result.fold( - (failure) => emit(DailyForecastError(failure.message)), - (forecast) { - emit(DailyForecastLoaded(_parseWeatherData(forecast))); - }, - ); - } - - List _parseWeatherData(Forecast5DaysModel forecast) { - List dailyForecasts = []; - String currentDate = ""; - WeatherTheme theme; - - for (var item in forecast.list) { - String date = item.date; - String humidity = "humidity ${item.main.humidity}%"; - String temperature = convertTemperatureToCelsius(item.main.temp); - String description = item.weather[0].description; - String main = item.weather[0].main; - theme = WeatherTheme.mapWeatherStateToTheme( - item.weather[0].main.mapToWeatherState()); - if (date.substring(0, 10) != currentDate) { - date = item.date.substring(0, 10); - dailyForecasts.add(DailyForecast( - date: date, - humidity: humidity, - temperature: temperature, - description: description, - main: main, - image: - GlobalVariablesState.isNight ? theme.nightImage : theme.dayImage, - textColor: theme.textColor, - )); - currentDate = date; - } - } - return (dailyForecasts); - } -} diff --git a/lib/features/daily_forecast/cubit/daily_forecast_state.dart b/lib/features/daily_forecast/cubit/daily_forecast_state.dart deleted file mode 100644 index 95aff46..0000000 --- a/lib/features/daily_forecast/cubit/daily_forecast_state.dart +++ /dev/null @@ -1,22 +0,0 @@ -part of 'daily_forecast_cubit.dart'; - -abstract class DailyForecastState extends Equatable { - const DailyForecastState(); - - @override - List get props => []; -} - -class DailyForecastInitial extends DailyForecastState {} - -class DailyForecastLoaded extends DailyForecastState { - final List forecast; - - const DailyForecastLoaded(this.forecast); -} - -class DailyForecastError extends DailyForecastState { - final String errorMessage; - - const DailyForecastError(this.errorMessage); -} diff --git a/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart b/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart new file mode 100644 index 0000000..356562a --- /dev/null +++ b/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart @@ -0,0 +1,77 @@ +import 'package:clima/core/global/enums.dart'; +import 'package:clima/core/helper/converter_helper.dart'; +import 'package:clima/core/helper/location_helper.dart'; +import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; + +import '../../hourly_forecast/data/models/hourly_forecast_model.dart'; +import '../data/models/daily_forecast_model.dart'; + +part 'detailed_forecast_state.dart'; + +class DetailedForecastCubit extends Cubit { + DetailedForecastCubit(this._repository) : super(DetailedForecastInitial()); + final DetailedForecastRepository _repository; + DateTime? todayDate; + DateTime? todayHours; + fetchWeatherData() async { + var result = await _repository.fetchWeatherData( + Location.instance.position?.latitude, + Location.instance.position?.longitude); + result.fold( + (failure) => emit(DetailedForecastError(failure.message)), + (forecast) { + List dailyForecast = []; + List hourlyForecast = []; + if (forecast.daily != null) { + for (int i = 0; i < forecast.daily!.time!.length; i++) { + dailyForecast.add(DailyForecast( + time: forecast.daily!.time![i].toString(), + weatherCode: forecast.daily!.weatherCode![i], + sunrise: forecast.daily!.sunrise![i], + sunset: forecast.daily!.sunset![i], + daylightDuration: forecast.daily!.daylightDuration![i], + sunshineDuration: forecast.daily!.sunshineDuration![i], + uvIndexMax: forecast.daily!.uvIndexMax![i], + )); + } + } else { + emit(const DetailedForecastError("Error processing forecast data")); + } + if (forecast.hourly != null) { + todayDate = DateTime.parse(forecast.hourly!.time![0]); + for (int i = 0; i < forecast.hourly!.time!.length; i++) { + todayHours = DateTime.parse(forecast.hourly!.time![i]); + hourlyForecast.add( + HourlyForecast( + todayDate: DateFormat('EEEE, d MMMM').format(todayDate!), + hour: DateFormat('h a').format(todayHours!), + temperature: forecast.hourly!.temperature2M![i], + humidity: forecast.hourly!.relativeHumidity2M![i], + weatherCode: forecast.hourly!.weatherCode![i], + image: forecast.hourly!.isDay![i] == 1 + ? getDayImage( + forecast.hourly!.weatherCode![i].mapToWeatherState()) + : getNightImage( + forecast.hourly!.weatherCode![i].mapToWeatherState()), + daylightDuration: forecast.daily!.daylightDuration![0], + sunshineDuration: forecast.daily!.sunshineDuration![0], + sunrise: forecast.daily!.sunrise![0], + sunset: forecast.daily!.sunset![0], + uvIndexMax: forecast.daily!.uvIndexMax![0], + ), + ); + } + } else { + emit(const DetailedForecastError("Error processing forecast data")); + } + emit(DetailsForecastSuccess( + dailyForecast: dailyForecast, + hourlyForecast: hourlyForecast, + )); + }, + ); + } +} diff --git a/lib/features/daily_forecast/cubit/detailed_forecast_state.dart b/lib/features/daily_forecast/cubit/detailed_forecast_state.dart new file mode 100644 index 0000000..452faea --- /dev/null +++ b/lib/features/daily_forecast/cubit/detailed_forecast_state.dart @@ -0,0 +1,27 @@ +part of 'detailed_forecast_cubit.dart'; + +abstract class DetailedForecastState extends Equatable { + const DetailedForecastState(); + + @override + List get props => []; +} + +class DetailedForecastInitial extends DetailedForecastState {} + +class DetailsForecastSuccess extends DetailedForecastState { + final List dailyForecast; + final List hourlyForecast; + const DetailsForecastSuccess( + {required this.dailyForecast, required this.hourlyForecast}); +} + +class HourlyForecastState extends DetailedForecastState { + const HourlyForecastState(); +} + +class DetailedForecastError extends DetailedForecastState { + final String errorMessage; + + const DetailedForecastError(this.errorMessage); +} diff --git a/lib/features/daily_forecast/data/models/daily_forecast_model.dart b/lib/features/daily_forecast/data/models/daily_forecast_model.dart index e6982f5..3347bfd 100644 --- a/lib/features/daily_forecast/data/models/daily_forecast_model.dart +++ b/lib/features/daily_forecast/data/models/daily_forecast_model.dart @@ -1,21 +1,19 @@ -import 'dart:ui'; - class DailyForecast { - String date; - String humidity; - String temperature; - String description; - String main; - String image; - Color textColor; + String? time; + int? weatherCode; + String? sunrise; + String? sunset; + double? daylightDuration; + double? sunshineDuration; + double? uvIndexMax; DailyForecast({ - required this.date, - required this.humidity, - required this.temperature, - required this.description, - required this.main, - required this.image, - required this.textColor, + this.time, + this.weatherCode, + this.sunrise, + this.sunset, + this.daylightDuration, + this.sunshineDuration, + this.uvIndexMax, }); } diff --git a/lib/features/daily_forecast/data/models/weather_data.dart b/lib/features/daily_forecast/data/models/weather_data.dart new file mode 100644 index 0000000..9c63acd --- /dev/null +++ b/lib/features/daily_forecast/data/models/weather_data.dart @@ -0,0 +1,97 @@ +class OpenMeteoResponse { + Hourly? hourly; + Daily? daily; + + OpenMeteoResponse({ + this.hourly, + this.daily, + }); + + factory OpenMeteoResponse.fromJson(Map json) => + OpenMeteoResponse( + hourly: json["hourly"] == null ? null : Hourly.fromJson(json["hourly"]), + daily: json["daily"] == null ? null : Daily.fromJson(json["daily"]), + ); +} + +class Daily { + List? time; + List? weatherCode; + List? sunrise; + List? sunset; + List? daylightDuration; + List? sunshineDuration; + List? uvIndexMax; + + Daily({ + this.time, + this.weatherCode, + this.sunrise, + this.sunset, + this.daylightDuration, + this.sunshineDuration, + this.uvIndexMax, + }); + + factory Daily.fromJson(Map json) => Daily( + time: json["time"] == null + ? [] + : List.from(json["time"]!.map((x) => DateTime.parse(x))), + weatherCode: json["weather_code"] == null + ? [] + : List.from(json["weather_code"]!.map((x) => x)), + sunrise: json["sunrise"] == null + ? [] + : List.from(json["sunrise"]!.map((x) => x)), + sunset: json["sunset"] == null + ? [] + : List.from(json["sunset"]!.map((x) => x)), + daylightDuration: json["daylight_duration"] == null + ? [] + : List.from( + json["daylight_duration"]!.map((x) => x?.toDouble() ?? 0.0)), + sunshineDuration: json["sunshine_duration"] == null + ? [] + : List.from( + json["sunshine_duration"]!.map((x) => x?.toDouble() ?? 0.0)), + uvIndexMax: json["uv_index_max"] == null + ? [] + : List.from( + json["uv_index_max"]!.map((x) => x?.toDouble() ?? 0.0)), + ); +} + +class Hourly { + List? time; + List? temperature2M; + List? relativeHumidity2M; + List? weatherCode; + List? isDay; + + Hourly({ + this.time, + this.temperature2M, + this.relativeHumidity2M, + this.weatherCode, + this.isDay, + }); + + factory Hourly.fromJson(Map json) => Hourly( + time: json["time"] == null + ? [] + : List.from(json["time"]!.map((x) => x)), + temperature2M: json["temperature_2m"] == null + ? [] + : List.from( + json["temperature_2m"]!.map((x) => x?.toDouble())), + relativeHumidity2M: json["relative_humidity_2m"] == null + ? [] + : List.from(json["relative_humidity_2m"]!.map((x) => x)), + weatherCode: json["weather_code"] == null + ? [] + : List.from(json["weather_code"]!.map((x) => x)), + isDay: json["is_day"] == null + ? [] + : List.from(json["is_day"]!.map((x) => x)), + ); +} diff --git a/lib/features/daily_forecast/data/repo/daily_forecast_repo.dart b/lib/features/daily_forecast/data/repo/daily_forecast_repo.dart deleted file mode 100644 index f474d35..0000000 --- a/lib/features/daily_forecast/data/repo/daily_forecast_repo.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:clima/core/error/error_handling.dart'; -import 'package:fpdart/fpdart.dart'; - -import '../models/forecast_5_days_model.dart'; - -abstract class DailyForecastRepository { - Future> fetchForecast5Days( - double? latitude, double? longitude); -} diff --git a/lib/features/daily_forecast/data/repo/daily_forecast_repo_impl.dart b/lib/features/daily_forecast/data/repo/daily_forecast_repo_impl.dart deleted file mode 100644 index 7919c8f..0000000 --- a/lib/features/daily_forecast/data/repo/daily_forecast_repo_impl.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:clima/core/error/error_handling.dart'; -import 'package:clima/features/daily_forecast/data/repo/daily_forecast_repo.dart'; -import 'package:dio/dio.dart'; -import 'package:fpdart/fpdart.dart'; - -import '../../../../core/api/end_points.dart'; -import '../../../../core/services/api_service.dart'; -import '../models/forecast_5_days_model.dart'; - -class DailyForecastRepoImpl extends DailyForecastRepository { - final ApiService client; - - DailyForecastRepoImpl(this.client); - @override - Future> fetchForecast5Days( - double? latitude, double? longitude) async { - try { - var result = - await client.get(endPoint: EndPoint.forecast5Days, parameters: { - 'lat': latitude.toString(), - 'lon': longitude.toString(), - }); - - final weather = Forecast5DaysModel.fromJson(result); - return right(weather); - } catch (e) { - print(Exception(e)); - if (e is DioException) { - return left(ServerFailure.handel(e)); - } else { - return left(ServerFailure("ERROR :: ${e.toString()}")); - } - } - } -} diff --git a/lib/features/daily_forecast/data/repo/detailed_forecast_repo.dart b/lib/features/daily_forecast/data/repo/detailed_forecast_repo.dart new file mode 100644 index 0000000..98704bf --- /dev/null +++ b/lib/features/daily_forecast/data/repo/detailed_forecast_repo.dart @@ -0,0 +1,9 @@ +import 'package:clima/core/error/error_handling.dart'; +import 'package:fpdart/fpdart.dart'; + +import '../models/weather_data.dart'; + +abstract class DetailedForecastRepository { + Future> fetchWeatherData( + double? latitude, double? longitude); +} diff --git a/lib/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart b/lib/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart new file mode 100644 index 0000000..a8e15df --- /dev/null +++ b/lib/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart @@ -0,0 +1,24 @@ +import 'package:clima/core/error/error_handling.dart'; +import 'package:clima/features/daily_forecast/data/models/weather_data.dart'; +import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; +import 'package:dio/dio.dart'; +import 'package:fpdart/fpdart.dart'; + +class DetailedForecastRepoImpl extends DetailedForecastRepository { + @override + Future> fetchWeatherData( + double? latitude, double? longitude) async { + try { + var result = await Dio().get( + "https://api.open-meteo.com/v1/forecast?latitude=$latitude&longitude=$longitude¤t=temperature_2m,is_day,weather_code&hourly=temperature_2m,relative_humidity_2m,weather_code,is_day&daily=weather_code,sunrise,sunset,daylight_duration,sunshine_duration,uv_index_max&timezone=auto&forecast_days=16&forecast_hours=24"); + final data = OpenMeteoResponse.fromJson(result.data); + return right(data); + } catch (e) { + if (e is DioException) { + return left(ServerFailure.handel(e)); + } else { + return left(ServerFailure("ERROR :: ${e.toString()}")); + } + } + } +} diff --git a/lib/features/daily_forecast/screens/daily_forecast_screen.dart b/lib/features/daily_forecast/screens/daily_forecast_screen.dart index 0895f41..8f3cea7 100644 --- a/lib/features/daily_forecast/screens/daily_forecast_screen.dart +++ b/lib/features/daily_forecast/screens/daily_forecast_screen.dart @@ -1,6 +1,6 @@ import 'package:clima/core/common/failure_widget.dart'; import 'package:clima/core/common/loading_widget.dart'; -import 'package:clima/features/daily_forecast/cubit/daily_forecast_cubit.dart'; +import 'package:clima/features/daily_forecast/cubit/detailed_forecast_cubit.dart'; import 'package:clima/features/daily_forecast/screens/widgets/forecast_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -10,13 +10,13 @@ class Forecast5DaysScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { - if (state is DailyForecastInitial) { + if (state is DetailedForecastInitial) { return const LoadingWidget(); - } else if (state is DailyForecastLoaded) { - return ForecastWidget(forecast: state.forecast); - } else if (state is DailyForecastError) { + } else if (state is DetailsForecastSuccess) { + return ForecastWidget(forecast: state.dailyForecast); + } else if (state is DetailedForecastError) { return FailureWidget(text: state.errorMessage); } else { return const FailureWidget(); diff --git a/lib/features/daily_forecast/screens/widgets/daily_weather_list.dart b/lib/features/daily_forecast/screens/widgets/daily_weather_list.dart index cf94197..c5482a2 100644 --- a/lib/features/daily_forecast/screens/widgets/daily_weather_list.dart +++ b/lib/features/daily_forecast/screens/widgets/daily_weather_list.dart @@ -1,10 +1,6 @@ -import 'package:clima/core/utils/utils.dart'; import 'package:flutter/material.dart'; -import '../../../../core/common/temperature_text.dart'; -import '../../../home/screens/widgets/widgets.dart'; import '../../data/models/daily_forecast_model.dart'; -import 'custom_shape.dart'; class DailyWeatherList extends StatelessWidget { const DailyWeatherList({ @@ -19,56 +15,8 @@ class DailyWeatherList extends StatelessWidget { return SliverList( delegate: SliverChildBuilderDelegate( (context, index) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Stack( - children: [ - CustomPaint( - size: Size( - AppDimensions.width!, - (AppDimensions.width! * 0.4).toDouble(), - ), - painter: CustomShape(context: context), - ), - Positioned( - top: 0, - right: 16, - child: SizedBox( - height: AppDimensions.height! * 0.17, - child: WeatherImage(image: forecast[index].image), - ), - ), - Positioned( - top: 16, - left: 16, - child: TemperatureText( - temperature: forecast[index].temperature, - style: AppTypography.bold56(), - ), - ), - Positioned( - bottom: 16, - left: 16, - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: forecast[index].main, - style: AppTypography.medium24( - color: forecast[index].textColor, - ), - ), - TextSpan( - text: index == 0 - ? " • Today" - : " • ${forecast[index].date}", - ), - ], - ), - ), - ), - ], - ), + return const Center( + child: Text("No Data Yet"), ); }, childCount: forecast.length, @@ -76,3 +24,55 @@ class DailyWeatherList extends StatelessWidget { ); } } + +//Padding( +// padding: const EdgeInsets.all(8.0), +// child: Stack( +// children: [ +// CustomPaint( +// size: Size( +// AppDimensions.width!, +// (AppDimensions.width! * 0.4).toDouble(), +// ), +// painter: CustomShape(context: context), +// ), +// Positioned( +// top: 0, +// right: 16, +// child: SizedBox( +// height: AppDimensions.height! * 0.17, +// child: WeatherImage(image: forecast[index].image), +// ), +// ), +// Positioned( +// top: 16, +// left: 16, +// child: TemperatureText( +// temperature: forecast[index].temperature, +// style: AppTypography.bold56(), +// ), +// ), +// Positioned( +// bottom: 16, +// left: 16, +// child: Text.rich( +// TextSpan( +// children: [ +// TextSpan( +// text: forecast[index].main, +// style: AppTypography.medium24( +// color: forecast[index].textColor, +// ), +// ), +// TextSpan( +// text: index == 0 +// ? " • Today" +// : " • ${forecast[index].date}", +// ), +// ], +// ), +// ), +// ), +// ], +// ), +// ); diff --git a/lib/features/home/cubit/home_cubit.dart b/lib/features/home/cubit/home_cubit.dart index e4abe5b..031ba94 100644 --- a/lib/features/home/cubit/home_cubit.dart +++ b/lib/features/home/cubit/home_cubit.dart @@ -14,14 +14,15 @@ import '../data/repo/home_repo.dart'; part 'home_state.dart'; class HomeCubit extends Cubit { - HomeRepository repository; - var model; - HomeCubit(this.repository) : super(HomeLoadingState()); + HomeRepository _repository; + final double? _lat = Location.instance.position?.latitude; + final double? _long = Location.instance.position?.longitude; + Weather? _model; + HomeCubit(this._repository) : super(HomeLoadingState()); - getTodayWeather() async { - final data = await repository.getTodayWeather( - Location.instance.position?.latitude, - Location.instance.position?.longitude); + + Future getWeatherData() async { + final data = await _repository.getTodayWeather(_lat,_long); try { data.fold((error) { emit(HomeErrorState(error: error.message)); @@ -33,7 +34,7 @@ class HomeCubit extends Cubit { WeatherTheme theme = WeatherTheme.mapWeatherStateToTheme( weather.weatherState.mapToWeatherState()); // - model = Weather( + _model = Weather( convertTemperatureToCelsius(weather.temperature.toDouble()), convertTimeToReadableDate(weather.time.toInt()), GlobalVariablesState.isNight ? theme.nightImage : theme.dayImage, @@ -41,11 +42,11 @@ class HomeCubit extends Cubit { weather.weatherState, Location.instance.city); emit( - HomeSuccessState(weatherData: model, sys: weather.sys), + HomeSuccessState(weatherData: _model!, sys: weather.sys), ); }); } catch (e) { - print("/////////////////////////////////////////////\n $e"); + print(e); } } -} +} \ No newline at end of file diff --git a/lib/features/home/data/repo/home_repo.dart b/lib/features/home/data/repo/home_repo.dart index 91204a9..6687ec9 100644 --- a/lib/features/home/data/repo/home_repo.dart +++ b/lib/features/home/data/repo/home_repo.dart @@ -1,6 +1,6 @@ -import 'package:clima/core/error/error_handling.dart'; import 'package:fpdart/fpdart.dart'; +import '../../../../core/error/error_handling.dart'; import '../model/weather_model.dart'; abstract class HomeRepository { diff --git a/lib/features/home/data/repo/home_repo_impl.dart b/lib/features/home/data/repo/home_repo_impl.dart index 0392518..b9dc013 100644 --- a/lib/features/home/data/repo/home_repo_impl.dart +++ b/lib/features/home/data/repo/home_repo_impl.dart @@ -1,11 +1,11 @@ import 'package:clima/core/error/error_handling.dart'; -import 'package:clima/core/services/api_service.dart'; import 'package:clima/features/home/data/model/weather_model.dart'; import 'package:clima/features/home/data/repo/home_repo.dart'; import 'package:dio/dio.dart'; import 'package:fpdart/fpdart.dart'; import '../../../../core/api/end_points.dart'; +import '../../../../core/services/api_service.dart'; class HomeRepoImpl extends HomeRepository { final ApiService client; diff --git a/lib/features/hourly_forecast/cubit/hourly_forecast_cubit.dart b/lib/features/hourly_forecast/cubit/hourly_forecast_cubit.dart deleted file mode 100644 index 7095695..0000000 --- a/lib/features/hourly_forecast/cubit/hourly_forecast_cubit.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:clima/core/global/enums.dart'; -import 'package:clima/core/helper/converter_helper.dart'; -import 'package:clima/core/helper/location_helper.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../../core/global/variables.dart'; -import '../../../core/helper/functions.dart'; -import '../../daily_forecast/data/models/daily_weather_theme.dart'; -import '../../daily_forecast/data/models/forecast_5_days_model.dart'; -import '../../daily_forecast/data/repo/daily_forecast_repo.dart'; -import '../data/models/hourly_forecast_model.dart'; - -part 'hourly_forecast_state.dart'; - -class HourlyForecastCubit extends Cubit { - HourlyForecastCubit(this._repository) : super(HourlyForecastInitial()); - final DailyForecastRepository _repository; - fetchForecast5DaysData() async { - var result = await _repository.fetchForecast5Days( - Location.instance.position?.latitude, - Location.instance.position?.longitude); - result.fold( - (failure) => emit(HourlyForecastError(failure.message)), - (forecast) { - emit(HourlyForecastLoaded(_parseHourlyWeatherData(forecast))); - }, - ); - } - - List _parseHourlyWeatherData(Forecast5DaysModel forecast) { - List hourlyForecasts = []; - - String currentDay = DateTime.now().toString().substring(0, 10); - - for (var item in forecast.list) { - String date = item.date; - - // Check if the item's date matches the current day - if (date.substring(0, 10) == currentDay) { - String humidity = "${item.main.humidity}%"; - String temperature = convertTemperatureToCelsius(item.main.temp); - String description = item.weather[0].description; - String main = item.weather[0].main; - DailyWeatherTheme theme = DailyWeatherTheme.fromWeatherState( - item.weather[0].main.mapToWeatherState()); - hourlyForecasts.add(HourlyForecast( - hours: DateFormatter.formatHours(date), - day: DateFormatter.formatDay(date), - humidity: humidity, - temperature: temperature, - description: description, - data: item.main, - main: main, - image: - GlobalVariablesState.isNight ? theme.nightImage : theme.dayImage, - isExpanded: forecast.list[0] == item ? true : false, - )); - } - } - return hourlyForecasts; - } -} diff --git a/lib/features/hourly_forecast/cubit/hourly_forecast_state.dart b/lib/features/hourly_forecast/cubit/hourly_forecast_state.dart deleted file mode 100644 index fc3538e..0000000 --- a/lib/features/hourly_forecast/cubit/hourly_forecast_state.dart +++ /dev/null @@ -1,22 +0,0 @@ -part of 'hourly_forecast_cubit.dart'; - -abstract class HourlyForecastState extends Equatable { - const HourlyForecastState(); - - @override - List get props => []; -} - -class HourlyForecastInitial extends HourlyForecastState {} - -class HourlyForecastLoaded extends HourlyForecastState { - final List forecast; - - const HourlyForecastLoaded(this.forecast); -} - -class HourlyForecastError extends HourlyForecastState { - final String errorMessage; - - const HourlyForecastError(this.errorMessage); -} diff --git a/lib/features/hourly_forecast/data/models/hourly_forecast_model.dart b/lib/features/hourly_forecast/data/models/hourly_forecast_model.dart index e81281e..2c7200c 100644 --- a/lib/features/hourly_forecast/data/models/hourly_forecast_model.dart +++ b/lib/features/hourly_forecast/data/models/hourly_forecast_model.dart @@ -1,24 +1,27 @@ -import '../../../daily_forecast/data/models/forecast_5_days_model.dart'; - class HourlyForecast { - String hours; - String day; - String humidity; - String temperature; - String description; - String main; - String image; - MainWeatherData data; - bool isExpanded; + String? todayDate; + String? hour; + double? temperature; + int? humidity; + int? weatherCode; + String? image; + double? daylightDuration; + double? sunshineDuration; + String? sunrise; + String? sunset; + double? uvIndexMax; - HourlyForecast( - {required this.hours, - required this.day, - required this.humidity, - required this.temperature, - required this.description, - required this.main, - required this.image, - required this.data, - required this.isExpanded}); + HourlyForecast({ + this.todayDate, + this.hour, + this.temperature, + this.humidity, + this.weatherCode, + this.image, + this.daylightDuration, + this.sunshineDuration, + this.sunrise, + this.sunset, + this.uvIndexMax, + }); } diff --git a/lib/features/hourly_forecast/screens/hourly_forecast_screen.dart b/lib/features/hourly_forecast/screens/hourly_forecast_screen.dart index 9b786ae..80c9028 100644 --- a/lib/features/hourly_forecast/screens/hourly_forecast_screen.dart +++ b/lib/features/hourly_forecast/screens/hourly_forecast_screen.dart @@ -1,25 +1,26 @@ import 'package:clima/core/common/failure_widget.dart'; import 'package:clima/core/common/loading_widget.dart'; -import 'package:clima/features/hourly_forecast/cubit/hourly_forecast_cubit.dart'; import 'package:clima/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../daily_forecast/cubit/detailed_forecast_cubit.dart'; + class HourlyForecastScreen extends StatelessWidget { const HourlyForecastScreen({super.key}); @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { - if (state is HourlyForecastInitial) { + if (state is DetailedForecastInitial) { return const LoadingWidget(); - } else if (state is HourlyForecastError) { + } else if (state is DetailsForecastSuccess) { + return HourlyForecastWidget(forecast: state.hourlyForecast); + } else if (state is DetailedForecastError) { return FailureWidget(text: state.errorMessage); - } else if (state is HourlyForecastLoaded) { - return HourlyForecastWidget(forecast: state.forecast); } else { - return const Center(child: Text("Something Wrong")); + return const FailureWidget(); } }, ); diff --git a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart index 3cfba4e..7c43a62 100644 --- a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart +++ b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart @@ -31,18 +31,18 @@ class HourlyForecastDetails extends StatelessWidget { child: Column( children: [ Expanded( - child: Text(forecastList[index].hours), + child: Text(forecastList[index].hour!), ), Expanded( flex: 3, child: RepaintBoundary( - child: Lottie.asset(forecastList[index].image), + child: Lottie.asset(forecastList[index].image!), ), ), const SizedBox(height: 8), Expanded( child: Text( - forecastList[index].temperature, + forecastList[index].temperature!.toString(), style: AppTypography.medium14(), ), ), @@ -51,7 +51,7 @@ class HourlyForecastDetails extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.water_drop_outlined, size: 16), - Text(forecastList[index].humidity), + Text(forecastList[index].humidity.toString()), ], ), ), diff --git a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart index 5f7b8af..9a37140 100644 --- a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart +++ b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart @@ -19,7 +19,7 @@ class _HourlyForecastWidgetState extends State { appBar: AppBar( title: const Text("Today's details"), actions: [ - Text(widget.forecast[0].day), + Text(widget.forecast[0].todayDate!), const SizedBox(width: 8), ], ), diff --git a/lib/features/landing_page/landing_screen.dart b/lib/features/landing_page/landing_screen.dart index ec974f1..b542510 100644 --- a/lib/features/landing_page/landing_screen.dart +++ b/lib/features/landing_page/landing_screen.dart @@ -1,9 +1,8 @@ import 'package:clima/core/services/get_it_service.dart'; -import 'package:clima/features/daily_forecast/cubit/daily_forecast_cubit.dart'; -import 'package:clima/features/daily_forecast/data/repo/daily_forecast_repo.dart'; +import 'package:clima/features/daily_forecast/cubit/detailed_forecast_cubit.dart'; +import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; import 'package:clima/features/home/cubit/home_cubit.dart'; import 'package:clima/features/home/data/repo/home_repo.dart'; -import 'package:clima/features/hourly_forecast/cubit/hourly_forecast_cubit.dart'; import 'package:clima/features/landing_page/widgets/bottom_nav_bar_list.dart'; import 'package:clima/features/landing_page/widgets/location_service_disabled.dart'; import 'package:clima/features/landing_page/widgets/permission_denied_widget.dart'; @@ -30,17 +29,12 @@ class LandingScreen extends StatelessWidget { ), BlocProvider( create: (context) => - HomeCubit(getIt.get())..getTodayWeather(), + HomeCubit(getIt.get())..getWeatherData(), ), BlocProvider( create: (context) => - DailyForecastCubit(getIt.get()) - ..fetchForecast5DaysData(), - ), - BlocProvider( - create: (context) => - HourlyForecastCubit(getIt.get()) - ..fetchForecast5DaysData(), + DetailedForecastCubit(getIt.get()) + ..fetchWeatherData(), ), ], child: BlocBuilder( @@ -91,8 +85,7 @@ class LandingScreen extends StatelessWidget { } void fetchData(BuildContext context) async { - BlocProvider.of(context).fetchForecast5DaysData(); - BlocProvider.of(context).fetchForecast5DaysData(); - BlocProvider.of(context).getTodayWeather(); + BlocProvider.of(context).fetchWeatherData(); + BlocProvider.of(context).getWeatherData(); } } diff --git a/pubspec.lock b/pubspec.lock index 54387e0..93410f9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -384,6 +384,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.15.4" + http: + dependency: "direct main" + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" http_parser: dependency: transitive description: @@ -400,6 +408,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.0.8" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" js: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index edcc70f..7a8ea57 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: flutter_bloc: ^8.1.3 get_it: ^7.6.4 lottie: ^2.7.0 + http: iconsax: ^0.0.8 flutter_local_notifications: ^16.1.0 path_provider: ^2.1.1 @@ -26,6 +27,7 @@ dependencies: connectivity_plus: ^5.0.1 geocoding: ^2.1.1 google_maps_flutter: ^2.5.0 + intl: dev_dependencies: flutter_test: From f2f4e40d17a4b0780b9c48bba74ed4680423ef37 Mon Sep 17 00:00:00 2001 From: hab Date: Sat, 6 Jan 2024 10:51:24 +0200 Subject: [PATCH 3/4] change home api --- lib/app/bloc/theme/theme_cubit.dart | 8 +- lib/core/common/temperature_text.dart | 25 +++---- lib/core/common/weather_image.dart | 20 ++--- lib/core/error/error_handling.dart | 3 +- lib/core/extensions/map_weather_code.dart | 46 ++++++++++++ lib/core/global/enums.dart | 74 ------------------- lib/core/utils/app_decoration.dart | 10 +++ .../cubit/detailed_forecast_cubit.dart | 10 ++- lib/features/home/cubit/home_cubit.dart | 66 ++++++++--------- lib/features/home/cubit/home_state.dart | 5 +- .../home/data/model/weather_model.dart | 42 +++++++++++ .../home/data/model/weather_theme.dart | 33 ++++----- lib/features/home/data/repo/home_repo.dart | 3 + .../home/data/repo/home_repo_impl.dart | 12 +++ lib/features/home/screens/home_screen.dart | 21 +++--- .../home/screens/widgets/home_widget.dart | 12 +-- .../home/screens/widgets/widgets.dart | 6 +- .../widgets/hourly_forecast_details.dart | 6 +- .../widgets/hourly_forecast_widget.dart | 54 +++++++++++++- lib/features/landing_page/landing_screen.dart | 4 +- pubspec.lock | 26 +++---- 21 files changed, 285 insertions(+), 201 deletions(-) create mode 100644 lib/core/extensions/map_weather_code.dart create mode 100644 lib/core/utils/app_decoration.dart diff --git a/lib/app/bloc/theme/theme_cubit.dart b/lib/app/bloc/theme/theme_cubit.dart index 5b24045..da53c9a 100644 --- a/lib/app/bloc/theme/theme_cubit.dart +++ b/lib/app/bloc/theme/theme_cubit.dart @@ -7,11 +7,11 @@ part 'theme_state.dart'; class ThemeCubit extends Cubit { ThemeCubit() : super(AppThemes.basic); - void switchTheme(bool isNight) { - if (isNight) { - emit(AppThemes.dark); - } else { + void switchTheme(bool isDay) { + if (isDay) { emit(AppThemes.light); + } else { + emit(AppThemes.dark); } } } diff --git a/lib/core/common/temperature_text.dart b/lib/core/common/temperature_text.dart index 888ea8b..bed8dd0 100644 --- a/lib/core/common/temperature_text.dart +++ b/lib/core/common/temperature_text.dart @@ -1,7 +1,8 @@ import 'package:clima/core/utils/utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; -import '../global/variables.dart'; +import '../../features/home/cubit/home_cubit.dart'; class TemperatureText extends StatelessWidget { const TemperatureText({ @@ -18,19 +19,8 @@ class TemperatureText extends StatelessWidget { return GradientText( temperature!, style: style ?? AppTypography.bold144(), - gradient: GlobalVariablesState.isNight + gradient: context.read().isDay ? const LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - stops: [ - 0.3, - 1 - ], - colors: [ - AppColors.white, - AppColors.primary, - ]) - : const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [0.5, 1.0], @@ -38,6 +28,15 @@ class TemperatureText extends StatelessWidget { AppColors.primary, AppColors.white, ], + ) + : const LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + stops: [0.3, 1], + colors: [ + AppColors.white, + AppColors.primary, + ], ), ); } diff --git a/lib/core/common/weather_image.dart b/lib/core/common/weather_image.dart index 680616e..ae58453 100644 --- a/lib/core/common/weather_image.dart +++ b/lib/core/common/weather_image.dart @@ -25,7 +25,7 @@ class _WeatherImageState extends State vsync: this, ); - var tween = Tween(begin: -20.0, end: 20.0); + var tween = Tween(begin: -15.0, end: 40.0); _animation = tween.animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), @@ -47,21 +47,21 @@ class _WeatherImageState extends State child: Container( decoration: BoxDecoration( shape: BoxShape.circle, - gradient: GlobalVariablesState.isNight - ? const RadialGradient( + gradient: context.read().isDay + ? RadialGradient( colors: [ - Color(0xFFE2E0EF), - Colors.transparent, + const Color(0xFFEFC5B4), + const Color(0xFFF2E477).withOpacity(0.001), ], - stops: [0.1, 1.0], + stops: const [0.1, 1.0], radius: 0.5, ) - : RadialGradient( + : const RadialGradient( colors: [ - const Color(0xFFEFC5B4), - const Color(0xFFF2E477).withOpacity(0.001), + Color(0xFFE2E0EF), + Colors.transparent, ], - stops: const [0.1, 1.0], + stops: [0.1, 1.0], radius: 0.5, ), ), diff --git a/lib/core/error/error_handling.dart b/lib/core/error/error_handling.dart index 44b838b..65852e7 100644 --- a/lib/core/error/error_handling.dart +++ b/lib/core/error/error_handling.dart @@ -30,7 +30,8 @@ class ServerFailure extends IErrorHandler { } return ServerFailure('Unexpected Error, Please try again!'); default: - return ServerFailure('Oops! There was an Error, Please try again'); + return ServerFailure( + 'An unexpected error occurred. Please Refresh the page.'); } } diff --git a/lib/core/extensions/map_weather_code.dart b/lib/core/extensions/map_weather_code.dart new file mode 100644 index 0000000..5190c2f --- /dev/null +++ b/lib/core/extensions/map_weather_code.dart @@ -0,0 +1,46 @@ +import '../global/enums.dart'; + +extension MapWeatherCode on int { + WeatherState mapToWeatherState() { + switch (this) { + case 0: + return WeatherState.Clear; + case 1: + case 2: + case 3: + case 45: + case 48: + return WeatherState.Clouds; + case 51: + case 53: + case 55: + case 56: + case 57: + return WeatherState.Rain; + case 61: + case 63: + case 65: + case 66: + case 67: + return WeatherState.Rain; + case 71: + case 73: + case 75: + case 77: + return WeatherState.Snow; + case 80: + case 81: + case 82: + case 85: + case 86: + return WeatherState.Rain; + case 95: + return WeatherState.Storm; + case 96: + case 99: + return WeatherState.Storm; + default: + return WeatherState.Unknown; + } + } +} diff --git a/lib/core/global/enums.dart b/lib/core/global/enums.dart index 497116a..24d39a7 100644 --- a/lib/core/global/enums.dart +++ b/lib/core/global/enums.dart @@ -7,77 +7,3 @@ enum WeatherState { Clouds, Unknown, } - -extension MapWeatherState on String { - WeatherState mapToWeatherState() { - switch (toLowerCase()) { - case 'thunderstorm': - return WeatherState.Storm; - case 'drizzle': - case 'rain': - return WeatherState.Rain; - case 'snow': - return WeatherState.Snow; - case 'mist': - case 'smoke': - case 'haze': - case 'dust': - case 'sand': - case 'ash': - case 'squall': - case 'tornado': - return WeatherState.Wind; - case 'clear': - return WeatherState.Clear; - case 'clouds': - return WeatherState.Clouds; - default: - return WeatherState.Unknown; - } - } -} - -extension MapWeatherCode on int { - WeatherState mapToWeatherState() { - switch (this) { - case 0: - return WeatherState.Clear; - case 1: - case 2: - case 3: - case 45: - case 48: - return WeatherState.Clouds; - case 51: - case 53: - case 55: - case 56: - case 57: - return WeatherState.Rain; - case 61: - case 63: - case 65: - case 66: - case 67: - return WeatherState.Rain; - case 71: - case 73: - case 75: - case 77: - return WeatherState.Snow; - case 80: - case 81: - case 82: - case 85: - case 86: - return WeatherState.Rain; - case 95: - return WeatherState.Storm; - case 96: - case 99: - return WeatherState.Storm; - default: - return WeatherState.Unknown; - } - } -} diff --git a/lib/core/utils/app_decoration.dart b/lib/core/utils/app_decoration.dart new file mode 100644 index 0000000..a8f72de --- /dev/null +++ b/lib/core/utils/app_decoration.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class AppDecoration { + static BoxDecoration container(BuildContext context) { + return BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Theme.of(context).focusColor, + ); + } +} diff --git a/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart b/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart index 356562a..efd30b8 100644 --- a/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart +++ b/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart @@ -1,4 +1,4 @@ -import 'package:clima/core/global/enums.dart'; +import 'package:clima/core/extensions/map_weather_code.dart'; import 'package:clima/core/helper/converter_helper.dart'; import 'package:clima/core/helper/location_helper.dart'; import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; @@ -16,6 +16,8 @@ class DetailedForecastCubit extends Cubit { final DetailedForecastRepository _repository; DateTime? todayDate; DateTime? todayHours; + DateTime? sunset; + DateTime? sunrise; fetchWeatherData() async { var result = await _repository.fetchWeatherData( Location.instance.position?.latitude, @@ -42,6 +44,8 @@ class DetailedForecastCubit extends Cubit { } if (forecast.hourly != null) { todayDate = DateTime.parse(forecast.hourly!.time![0]); + sunset = DateTime.parse(forecast.daily!.sunset![0]); + sunrise = DateTime.parse(forecast.daily!.sunrise![0]); for (int i = 0; i < forecast.hourly!.time!.length; i++) { todayHours = DateTime.parse(forecast.hourly!.time![i]); hourlyForecast.add( @@ -58,8 +62,8 @@ class DetailedForecastCubit extends Cubit { forecast.hourly!.weatherCode![i].mapToWeatherState()), daylightDuration: forecast.daily!.daylightDuration![0], sunshineDuration: forecast.daily!.sunshineDuration![0], - sunrise: forecast.daily!.sunrise![0], - sunset: forecast.daily!.sunset![0], + sunrise: DateFormat('h : m a').format(sunrise!), + sunset: DateFormat('h : m a').format(sunset!), uvIndexMax: forecast.daily!.uvIndexMax![0], ), ); diff --git a/lib/features/home/cubit/home_cubit.dart b/lib/features/home/cubit/home_cubit.dart index 031ba94..33a0a1c 100644 --- a/lib/features/home/cubit/home_cubit.dart +++ b/lib/features/home/cubit/home_cubit.dart @@ -1,52 +1,48 @@ -import 'package:clima/core/global/enums.dart'; -import 'package:clima/core/helper/functions.dart'; +import 'package:clima/core/extensions/map_weather_code.dart'; import 'package:clima/core/helper/location_helper.dart'; import 'package:clima/core/utils/app_images.dart'; -import 'package:clima/features/home/data/model/weather.dart'; import 'package:clima/features/home/data/model/weather_model.dart'; import 'package:clima/features/home/data/model/weather_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; -import '../../../core/global/variables.dart'; import '../data/repo/home_repo.dart'; part 'home_state.dart'; class HomeCubit extends Cubit { - HomeRepository _repository; + final HomeRepository _repository; final double? _lat = Location.instance.position?.latitude; final double? _long = Location.instance.position?.longitude; - Weather? _model; + bool isDay = false; HomeCubit(this._repository) : super(HomeLoadingState()); + fetchWeatherData() async { + var result = await _repository.fetchCurrentWeather(_lat, _long); + result.fold( + (failure) => emit(HomeErrorState(error: failure.message)), + (response) { + if (response == null || + response.time == null || + response.temperature == null || + response.weatherCode == null || + response.isDay == null) { + emit(HomeErrorState(error: "Invalid response data")); + } + isDay = response.isDay == 1 ? true : false; - - Future getWeatherData() async { - final data = await _repository.getTodayWeather(_lat,_long); - try { - data.fold((error) { - emit(HomeErrorState(error: error.message)); - }, (weather) { - /// [isNight] is a global value => lib/core/constant/variables.dart - GlobalVariablesState.isNight = isNightTime( - weather.sys.sunrise.toInt(), weather.sys.sunset.toInt()); - // - WeatherTheme theme = WeatherTheme.mapWeatherStateToTheme( - weather.weatherState.mapToWeatherState()); - // - _model = Weather( - convertTemperatureToCelsius(weather.temperature.toDouble()), - convertTimeToReadableDate(weather.time.toInt()), - GlobalVariablesState.isNight ? theme.nightImage : theme.dayImage, - theme.textColor, - weather.weatherState, - Location.instance.city); - emit( - HomeSuccessState(weatherData: _model!, sys: weather.sys), - ); - }); - } catch (e) { - print(e); - } + emit(HomeSuccessState( + weather: WeatherData( + // to modify the date to appear as 'month, day, year' + date: DateFormat.yMMMMd().format(DateTime.parse(response.time)), + temperature: response.temperature.ceil(), + // contains the image and text color based on it's a day or night + theme: WeatherTheme.mapWeatherStateToTheme( + response.weatherCode.mapToWeatherState(), isDay), + weatherState: response.weatherCode.mapToWeatherState(), + isDay: isDay), + )); + }, + ); } -} \ No newline at end of file +} diff --git a/lib/features/home/cubit/home_state.dart b/lib/features/home/cubit/home_state.dart index da37b4a..729c111 100644 --- a/lib/features/home/cubit/home_state.dart +++ b/lib/features/home/cubit/home_state.dart @@ -11,9 +11,8 @@ class HomeLoadingState extends HomeState { } class HomeSuccessState extends HomeState { - final Weather weatherData; - final Sys sys; - HomeSuccessState({required this.weatherData, required this.sys}); + final WeatherData weather; + HomeSuccessState({required this.weather}); } class HomeErrorState extends HomeState { diff --git a/lib/features/home/data/model/weather_model.dart b/lib/features/home/data/model/weather_model.dart index 98b7d99..8b5e32d 100644 --- a/lib/features/home/data/model/weather_model.dart +++ b/lib/features/home/data/model/weather_model.dart @@ -1,3 +1,45 @@ +import 'package:clima/core/global/enums.dart'; +import 'package:clima/features/home/data/model/weather_theme.dart'; + +class OpenMeteoCurrentResponse { + String time; + num temperature; + int isDay; + int weatherCode; + + OpenMeteoCurrentResponse({ + required this.time, + required this.temperature, + required this.isDay, + required this.weatherCode, + }); + + factory OpenMeteoCurrentResponse.fromJson(Map json) { + return OpenMeteoCurrentResponse( + time: json['current']['time'], + temperature: json['current']['temperature_2m'], + isDay: json['current']['is_day'], + weatherCode: json['current']['weather_code'], + ); + } +} + +class WeatherData { + final String date; + final num temperature; + final WeatherState weatherState; + final WeatherTheme theme; + final bool isDay; + + WeatherData({ + required this.date, + required this.temperature, + required this.weatherState, + required this.theme, + required this.isDay, + }); +} + class WeatherModel { final num time; final String weatherState; diff --git a/lib/features/home/data/model/weather_theme.dart b/lib/features/home/data/model/weather_theme.dart index c8a774f..dc60f6e 100644 --- a/lib/features/home/data/model/weather_theme.dart +++ b/lib/features/home/data/model/weather_theme.dart @@ -4,51 +4,50 @@ import 'package:flutter/material.dart'; import '../../../../core/global/enums.dart'; class WeatherTheme { - final String dayImage; - final String nightImage; + final String image; final Color textColor; WeatherTheme({ - required this.dayImage, - required this.nightImage, + required this.image, required this.textColor, }); - factory WeatherTheme.mapWeatherStateToTheme(WeatherState state) { + + factory WeatherTheme.mapWeatherStateToTheme(WeatherState state, bool isDay) { + String getImageBasedOnDayTime(String dayImage, String nightImage) { + return isDay ? dayImage : nightImage; + } + switch (state) { case WeatherState.Storm: return WeatherTheme( - dayImage: AppImages.dayStorm, - nightImage: AppImages.nightStorm, + image: + getImageBasedOnDayTime(AppImages.dayStorm, AppImages.nightStorm), textColor: AppColors.thunderstorm, ); case WeatherState.Rain: return WeatherTheme( - dayImage: AppImages.dayRain, - nightImage: AppImages.nightRain, + image: getImageBasedOnDayTime(AppImages.dayRain, AppImages.nightRain), textColor: AppColors.rain, ); case WeatherState.Snow: return WeatherTheme( - dayImage: AppImages.daySnow, - nightImage: AppImages.nightSnow, + image: getImageBasedOnDayTime(AppImages.daySnow, AppImages.nightSnow), textColor: AppColors.snow, ); case WeatherState.Wind: return WeatherTheme( - dayImage: AppImages.dayWind, - nightImage: AppImages.nightWind, + image: getImageBasedOnDayTime(AppImages.dayWind, AppImages.nightWind), textColor: AppColors.wind, ); case WeatherState.Clear: return WeatherTheme( - dayImage: AppImages.daySun, - nightImage: AppImages.nightMoon, + image: getImageBasedOnDayTime(AppImages.daySun, AppImages.nightMoon), textColor: AppColors.clear, ); case WeatherState.Clouds: return WeatherTheme( - dayImage: AppImages.dayClouds, - nightImage: AppImages.nightClouds, + image: getImageBasedOnDayTime( + AppImages.dayClouds, AppImages.nightClouds), textColor: AppColors.cloud, ); case WeatherState.Unknown: diff --git a/lib/features/home/data/repo/home_repo.dart b/lib/features/home/data/repo/home_repo.dart index 6687ec9..492a969 100644 --- a/lib/features/home/data/repo/home_repo.dart +++ b/lib/features/home/data/repo/home_repo.dart @@ -6,4 +6,7 @@ import '../model/weather_model.dart'; abstract class HomeRepository { Future> getTodayWeather( double? latitude, double? longitude); + + Future> fetchCurrentWeather( + double? latitude, double? longitude); } diff --git a/lib/features/home/data/repo/home_repo_impl.dart b/lib/features/home/data/repo/home_repo_impl.dart index b9dc013..c2572d3 100644 --- a/lib/features/home/data/repo/home_repo_impl.dart +++ b/lib/features/home/data/repo/home_repo_impl.dart @@ -31,4 +31,16 @@ class HomeRepoImpl extends HomeRepository { } } } + + @override + Future> fetchCurrentWeather( + double? latitude, double? longitude) async { + try { + var result = await Dio().get( + "https://api.open-meteo.com/v1/forecast?latitude=$latitude&longitude=$longitude¤t=temperature_2m,is_day,weather_code&timezone=auto&forecast_days=1&forecast_hours=24"); + return right(OpenMeteoCurrentResponse.fromJson(result.data)); + } catch (e) { + return left(ServerFailure(e.toString())); + } + } } diff --git a/lib/features/home/screens/home_screen.dart b/lib/features/home/screens/home_screen.dart index 3f025e9..c30011b 100644 --- a/lib/features/home/screens/home_screen.dart +++ b/lib/features/home/screens/home_screen.dart @@ -1,8 +1,6 @@ import 'package:clima/app/bloc/theme/theme_cubit.dart'; import 'package:clima/core/common/failure_widget.dart'; import 'package:clima/core/common/loading_widget.dart'; -import 'package:clima/core/global/variables.dart'; -import 'package:clima/core/services/notification_service.dart'; import 'package:clima/features/home/cubit/home_cubit.dart'; import 'package:clima/features/home/screens/widgets/widgets.dart'; import 'package:flutter/material.dart'; @@ -16,22 +14,21 @@ class HomeScreen extends StatelessWidget { return BlocConsumer( listener: (context, state) { if (state is HomeSuccessState) { - BlocProvider.of(context) - .switchTheme(GlobalVariablesState.isNight); - NotificationService.scheduleSunriseSunsetNotifications( - title: "Today's weather in ${state.weatherData.cityName}", - body: - "${state..weatherData.temperature} • ${state.weatherData.weatherState}", - sunriseTime: state.sys.sunrise.toInt(), - sunsetTime: state.sys.sunset.toInt(), - imageAssetPath: state.weatherData.image); + BlocProvider.of(context).switchTheme(state.weather.isDay); + // NotificationService.scheduleSunriseSunsetNotifications( + // title: "Today's weather in ${state.weatherData.cityName}", + // body: + // "${state..weatherData.temperature} • ${state.weatherData.weatherState}", + // sunriseTime: state.sys.sunrise.toInt(), + // sunsetTime: state.sys.sunset.toInt(), + // imageAssetPath: state.weatherData.image); } }, builder: (context, state) { if (state is HomeLoadingState) { return const LoadingWidget(); } else if (state is HomeSuccessState) { - return HomeWidget(weather: state.weatherData); + return HomeWidget(weather: state.weather); } else if (state is HomeErrorState) { return FailureWidget(text: state.error); } else { diff --git a/lib/features/home/screens/widgets/home_widget.dart b/lib/features/home/screens/widgets/home_widget.dart index 9d23b71..fca7aa8 100644 --- a/lib/features/home/screens/widgets/home_widget.dart +++ b/lib/features/home/screens/widgets/home_widget.dart @@ -5,7 +5,7 @@ class HomeWidget extends StatelessWidget { super.key, required this.weather, }); - final Weather weather; + final WeatherData weather; @override Widget build(BuildContext context) { return Padding( @@ -16,21 +16,21 @@ class HomeWidget extends StatelessWidget { Expanded( child: TimeAndLocation( date: weather.date, - cityName: weather.cityName, + cityName: Location.instance.city, ), ), Expanded( flex: 2, child: WeatherImage( - image: weather.image, + image: weather.theme.image, ), ), Expanded( flex: 2, child: WeatherStatus( - temperature: weather.temperature, - weatherState: weather.weatherState, - textColor: weather.textColor, + temperature: weather.temperature.toString(), + weatherState: weather.weatherState.name.toString(), + textColor: weather.theme.textColor, ), ), ], diff --git a/lib/features/home/screens/widgets/widgets.dart b/lib/features/home/screens/widgets/widgets.dart index 63be7d6..baca89b 100644 --- a/lib/features/home/screens/widgets/widgets.dart +++ b/lib/features/home/screens/widgets/widgets.dart @@ -1,8 +1,10 @@ -import 'package:clima/core/global/variables.dart'; +import 'package:clima/core/helper/location_helper.dart'; import 'package:clima/core/utils/utils.dart'; -import 'package:clima/features/home/data/model/weather.dart'; +import 'package:clima/features/home/cubit/home_cubit.dart'; +import 'package:clima/features/home/data/model/weather_model.dart'; import 'package:clima/features/map/screens/map_screen.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:iconsax/iconsax.dart'; import '../../../../core/common/temperature_text.dart'; diff --git a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart index 7c43a62..287719b 100644 --- a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart +++ b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart @@ -1,3 +1,4 @@ +import 'package:clima/core/utils/app_decoration.dart'; import 'package:clima/core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:lottie/lottie.dart'; @@ -22,10 +23,7 @@ class HourlyForecastDetails extends StatelessWidget { scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), itemBuilder: (context, index) => Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Theme.of(context).focusColor, - ), + decoration: AppDecoration.container(context), width: AppDimensions.width! * 0.25, padding: const EdgeInsets.all(8), child: Column( diff --git a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart index 9a37140..c85c2d3 100644 --- a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart +++ b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart @@ -1,3 +1,5 @@ +import 'package:clima/core/utils/app_decoration.dart'; +import 'package:clima/core/utils/app_dimn.dart'; import 'package:flutter/material.dart'; import '../../data/models/hourly_forecast_model.dart'; @@ -23,9 +25,57 @@ class _HourlyForecastWidgetState extends State { const SizedBox(width: 8), ], ), - body: Column( + body: SingleChildScrollView( + child: Column( + children: [ + SizedBox(height: AppDimensions.height! * 0.005), + HourlyForecastDetails(forecastList: widget.forecast), + SizedBox(height: AppDimensions.height! * 0.02), + DayAndNight( + sunset: widget.forecast[0].sunset!, + sunrise: widget.forecast[0].sunrise!, + ), + ], + ), + ), + ); + } +} + +class DayAndNight extends StatelessWidget { + const DayAndNight({ + super.key, + required this.sunset, + required this.sunrise, + }); + final String sunset; + final String sunrise; + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric(horizontal: AppDimensions.width! * 0.03), + decoration: AppDecoration.container(context), + width: AppDimensions.width, + height: AppDimensions.height! * 0.25, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - HourlyForecastDetails(forecastList: widget.forecast), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(sunrise), + ], + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(sunset), + ], + ), + ), ], ), ); diff --git a/lib/features/landing_page/landing_screen.dart b/lib/features/landing_page/landing_screen.dart index b542510..58e603b 100644 --- a/lib/features/landing_page/landing_screen.dart +++ b/lib/features/landing_page/landing_screen.dart @@ -29,7 +29,7 @@ class LandingScreen extends StatelessWidget { ), BlocProvider( create: (context) => - HomeCubit(getIt.get())..getWeatherData(), + HomeCubit(getIt.get())..fetchWeatherData(), ), BlocProvider( create: (context) => @@ -86,6 +86,6 @@ class LandingScreen extends StatelessWidget { void fetchData(BuildContext context) async { BlocProvider.of(context).fetchWeatherData(); - BlocProvider.of(context).getWeatherData(); + BlocProvider.of(context).fetchWeatherData(); } } diff --git a/pubspec.lock b/pubspec.lock index 93410f9..bc526e6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -468,10 +468,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" nested: dependency: transitive description: @@ -625,18 +625,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -665,10 +665,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" timezone: dependency: transitive description: @@ -729,10 +729,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" win32: dependency: transitive description: @@ -758,5 +758,5 @@ packages: source: hosted version: "6.3.0" sdks: - dart: ">=3.1.3 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.13.0" From 23f014af309b61140edafdb633c2e01c8650ed8c Mon Sep 17 00:00:00 2001 From: hab Date: Sun, 7 Jan 2024 19:47:26 +0200 Subject: [PATCH 4/4] change hourly api and update the ui a bit --- lib/core/common/weather_image.dart | 101 +++++++---- lib/core/extensions/map_weather_code.dart | 22 +++ lib/core/helper/functions.dart | 36 ++-- lib/core/services/get_it_service.dart | 8 +- lib/core/utils/app_typography.dart | 12 ++ lib/core/utils/utils.dart | 1 + .../cubit/detailed_forecast_cubit.dart | 81 --------- .../data/models/daily_forecast_model.dart | 19 -- .../data/models/daily_weather_theme.dart | 59 ------- .../data/models/forecast_5_days_model.dart | 163 ------------------ .../data/models/weather_data.dart | 97 ----------- .../data/repo/detailed_forecast_repo.dart | 9 - .../repo/detailed_forecast_repo_impl.dart | 24 --- .../screens/daily_forecast_screen.dart | 18 +- .../screens/widgets/daily_weather_list.dart | 6 +- .../screens/widgets/forecast_widget.dart | 7 +- .../home/screens/widgets/weather_status.dart | 2 +- .../cubit/hourly_forecast_cubit.dart | 78 +++++++++ .../cubit/hourly_forecast_state.dart} | 6 +- .../data/models/hourly_forecast_model.dart | 27 --- .../open_meteo_hourly_response_model.dart | 18 ++ .../data/models/weather_daily_model.dart | 69 ++++++++ .../data/models/weather_hourly_model.dart | 29 ++++ .../data/repo/hourly_forecast_repo.dart | 9 + .../data/repo/hourly_forecast_repo_impl.dart | 26 +++ .../screens/hourly_forecast_screen.dart | 9 +- .../screens/widgets/day_and_night_widget.dart | 62 +++++++ .../widgets/hourly_forecast_details.dart | 18 +- .../widgets/hourly_forecast_widget.dart | 111 +++++++----- lib/features/landing_page/landing_screen.dart | 8 +- pubspec.lock | 24 +-- pubspec.yaml | 3 +- 32 files changed, 526 insertions(+), 636 deletions(-) delete mode 100644 lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart delete mode 100644 lib/features/daily_forecast/data/models/daily_forecast_model.dart delete mode 100644 lib/features/daily_forecast/data/models/daily_weather_theme.dart delete mode 100644 lib/features/daily_forecast/data/models/forecast_5_days_model.dart delete mode 100644 lib/features/daily_forecast/data/models/weather_data.dart delete mode 100644 lib/features/daily_forecast/data/repo/detailed_forecast_repo.dart delete mode 100644 lib/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart create mode 100644 lib/features/hourly_forecast/cubit/hourly_forecast_cubit.dart rename lib/features/{daily_forecast/cubit/detailed_forecast_state.dart => hourly_forecast/cubit/hourly_forecast_state.dart} (82%) delete mode 100644 lib/features/hourly_forecast/data/models/hourly_forecast_model.dart create mode 100644 lib/features/hourly_forecast/data/models/open_meteo_hourly_response_model.dart create mode 100644 lib/features/hourly_forecast/data/models/weather_daily_model.dart create mode 100644 lib/features/hourly_forecast/data/models/weather_hourly_model.dart create mode 100644 lib/features/hourly_forecast/data/repo/hourly_forecast_repo.dart create mode 100644 lib/features/hourly_forecast/data/repo/hourly_forecast_repo_impl.dart create mode 100644 lib/features/hourly_forecast/screens/widgets/day_and_night_widget.dart diff --git a/lib/core/common/weather_image.dart b/lib/core/common/weather_image.dart index ae58453..8f9ffca 100644 --- a/lib/core/common/weather_image.dart +++ b/lib/core/common/weather_image.dart @@ -2,10 +2,16 @@ part of '../../features/home/screens/widgets/widgets.dart'; class WeatherImage extends StatefulWidget { final String? image; + double? begin; + double? end; - const WeatherImage({ + final bool isCenter; + WeatherImage({ Key? key, required this.image, + this.begin, + this.end, + this.isCenter = true, }) : super(key: key); @override @@ -25,7 +31,8 @@ class _WeatherImageState extends State vsync: this, ); - var tween = Tween(begin: -15.0, end: 40.0); + var tween = + Tween(begin: widget.begin ?? -10.0, end: widget.end ?? 45.0); _animation = tween.animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), @@ -41,37 +48,67 @@ class _WeatherImageState extends State @override Widget build(BuildContext context) { return RepaintBoundary( - child: Center( - child: Transform.translate( - offset: Offset(0, _animation.value), - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: context.read().isDay - ? RadialGradient( - colors: [ - const Color(0xFFEFC5B4), - const Color(0xFFF2E477).withOpacity(0.001), - ], - stops: const [0.1, 1.0], - radius: 0.5, - ) - : const RadialGradient( - colors: [ - Color(0xFFE2E0EF), - Colors.transparent, - ], - stops: [0.1, 1.0], - radius: 0.5, - ), + child: widget.isCenter + ? Center( + child: Transform.translate( + offset: Offset(0, _animation.value), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: context.read().isDay + ? RadialGradient( + colors: [ + const Color(0xFFEFC5B4), + const Color(0xFFF2E477).withOpacity(0.001), + ], + stops: const [0.1, 1.0], + radius: 0.5, + ) + : const RadialGradient( + colors: [ + Color(0xFFE2E0EF), + Colors.transparent, + ], + stops: [0.1, 1.0], + radius: 0.5, + ), + ), + child: Image.asset( + widget.image!, + fit: BoxFit.cover, + ), + ), + ), + ) + : Transform.translate( + offset: Offset(0, _animation.value), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: context.read().isDay + ? RadialGradient( + colors: [ + const Color(0xFFEFC5B4), + const Color(0xFFF2E477).withOpacity(0.001), + ], + stops: const [0.1, 1.0], + radius: 0.5, + ) + : const RadialGradient( + colors: [ + Color(0xFFE2E0EF), + Colors.transparent, + ], + stops: [0.1, 1.0], + radius: 0.5, + ), + ), + child: Image.asset( + widget.image!, + fit: BoxFit.cover, + ), + ), ), - child: Image.asset( - widget.image!, - fit: BoxFit.cover, - ), - ), - ), - ), ); } diff --git a/lib/core/extensions/map_weather_code.dart b/lib/core/extensions/map_weather_code.dart index 5190c2f..637edb7 100644 --- a/lib/core/extensions/map_weather_code.dart +++ b/lib/core/extensions/map_weather_code.dart @@ -1,4 +1,5 @@ import '../global/enums.dart'; +import '../utils/app_images.dart'; extension MapWeatherCode on int { WeatherState mapToWeatherState() { @@ -44,3 +45,24 @@ extension MapWeatherCode on int { } } } + +extension MapWeatherStateToImage on WeatherState { + String getImages(bool isDay) { + switch (this) { + case WeatherState.Storm: + return AppLottie.dailyStorm; + case WeatherState.Rain: + return isDay ? AppLottie.dailyDayRain : AppLottie.dailyNightRain; + case WeatherState.Snow: + return isDay ? AppLottie.dailyDaySnow : AppLottie.dailyNightSnow; + case WeatherState.Wind: + return AppLottie.dailyWind; + case WeatherState.Clear: + return isDay ? AppLottie.dailyDay : AppLottie.dailyNight; + case WeatherState.Clouds: + return isDay ? AppLottie.dailyDayCloud : AppLottie.dailyNightCloud; + default: + return AppLottie.dailyStorm; + } + } +} diff --git a/lib/core/helper/functions.dart b/lib/core/helper/functions.dart index 2202d65..7e69cf9 100644 --- a/lib/core/helper/functions.dart +++ b/lib/core/helper/functions.dart @@ -5,24 +5,30 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../services/get_it_service.dart'; import 'bloc_observer.dart'; -import 'converter_helper.dart'; -String convertTimeToReadableDate(int time) { - DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(time * 1000); - return DateFormatter.format(dateTime); -} +bool isNull(dynamic object, String property) { + List propertyParts = property.split('.'); + dynamic currentObject = object; -String convertTemperatureToCelsius(double temperature) { - double temperatureInCelsius = - TemperatureConverter.kelvinToCelsius(temperature); - return '${temperatureInCelsius.round()}°'; -} + for (String part in propertyParts) { + if (currentObject == null) { + return true; + } -// Check if it's night or day based on sunrise and sunset timestamps -bool isNightTime(int sunriseTimestamp, int sunsetTimestamp) { - int currentTimestamp = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000; - return currentTimestamp < sunriseTimestamp || - currentTimestamp > sunsetTimestamp; + if (currentObject is Map) { + currentObject = currentObject[part]; + } else if (currentObject is List) { + int? index = int.tryParse(part); + if (index != null && index >= 0 && index < currentObject.length) { + currentObject = currentObject[index]; + } else { + return true; // Invalid index or not a list + } + } else { + return true; // Unknown type + } + } + return false; } initialization() { diff --git a/lib/core/services/get_it_service.dart b/lib/core/services/get_it_service.dart index 0ec625c..66ed792 100644 --- a/lib/core/services/get_it_service.dart +++ b/lib/core/services/get_it_service.dart @@ -1,8 +1,8 @@ import 'package:clima/core/services/api_service.dart'; -import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; -import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart'; import 'package:clima/features/home/data/repo/home_repo.dart'; import 'package:clima/features/home/data/repo/home_repo_impl.dart'; +import 'package:clima/features/hourly_forecast/data/repo/hourly_forecast_repo.dart'; +import 'package:clima/features/hourly_forecast/data/repo/hourly_forecast_repo_impl.dart'; import 'package:get_it/get_it.dart'; final getIt = GetIt.instance; @@ -11,6 +11,6 @@ void setup() { getIt.registerLazySingleton(() => ApiService()); getIt.registerLazySingleton( () => HomeRepoImpl(getIt.get())); - getIt.registerLazySingleton( - () => DetailedForecastRepoImpl()); + getIt.registerLazySingleton( + () => HourlyForecastRepoImpl()); } diff --git a/lib/core/utils/app_typography.dart b/lib/core/utils/app_typography.dart index e87d5d0..417dd77 100644 --- a/lib/core/utils/app_typography.dart +++ b/lib/core/utils/app_typography.dart @@ -67,14 +67,26 @@ class AppTypography { color: color); // 14 static TextStyle medium14({Color? color}) => TextStyle( + fontFamily: _fontFamily, + fontSize: _sp14, + fontWeight: _w500, + color: color); + static TextStyle bold14({Color? color}) => TextStyle( fontFamily: _fontFamily, fontSize: _sp14, fontWeight: _bold, color: color); + // 12 static TextStyle medium12({Color? color}) => TextStyle( fontFamily: _fontFamily, fontSize: _sp12, fontWeight: _w400, color: color); + + static TextStyle thin14({Color? color}) => TextStyle( + fontFamily: _fontFamily, + fontSize: _sp14, + fontWeight: FontWeight.w300, + color: color); } diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 3d5a6cd..fce363d 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -1,4 +1,5 @@ export 'app_colors.dart'; +export 'app_decoration.dart'; export 'app_dimn.dart'; export 'app_images.dart'; export 'app_themes.dart'; diff --git a/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart b/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart deleted file mode 100644 index efd30b8..0000000 --- a/lib/features/daily_forecast/cubit/detailed_forecast_cubit.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:clima/core/extensions/map_weather_code.dart'; -import 'package:clima/core/helper/converter_helper.dart'; -import 'package:clima/core/helper/location_helper.dart'; -import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:intl/intl.dart'; - -import '../../hourly_forecast/data/models/hourly_forecast_model.dart'; -import '../data/models/daily_forecast_model.dart'; - -part 'detailed_forecast_state.dart'; - -class DetailedForecastCubit extends Cubit { - DetailedForecastCubit(this._repository) : super(DetailedForecastInitial()); - final DetailedForecastRepository _repository; - DateTime? todayDate; - DateTime? todayHours; - DateTime? sunset; - DateTime? sunrise; - fetchWeatherData() async { - var result = await _repository.fetchWeatherData( - Location.instance.position?.latitude, - Location.instance.position?.longitude); - result.fold( - (failure) => emit(DetailedForecastError(failure.message)), - (forecast) { - List dailyForecast = []; - List hourlyForecast = []; - if (forecast.daily != null) { - for (int i = 0; i < forecast.daily!.time!.length; i++) { - dailyForecast.add(DailyForecast( - time: forecast.daily!.time![i].toString(), - weatherCode: forecast.daily!.weatherCode![i], - sunrise: forecast.daily!.sunrise![i], - sunset: forecast.daily!.sunset![i], - daylightDuration: forecast.daily!.daylightDuration![i], - sunshineDuration: forecast.daily!.sunshineDuration![i], - uvIndexMax: forecast.daily!.uvIndexMax![i], - )); - } - } else { - emit(const DetailedForecastError("Error processing forecast data")); - } - if (forecast.hourly != null) { - todayDate = DateTime.parse(forecast.hourly!.time![0]); - sunset = DateTime.parse(forecast.daily!.sunset![0]); - sunrise = DateTime.parse(forecast.daily!.sunrise![0]); - for (int i = 0; i < forecast.hourly!.time!.length; i++) { - todayHours = DateTime.parse(forecast.hourly!.time![i]); - hourlyForecast.add( - HourlyForecast( - todayDate: DateFormat('EEEE, d MMMM').format(todayDate!), - hour: DateFormat('h a').format(todayHours!), - temperature: forecast.hourly!.temperature2M![i], - humidity: forecast.hourly!.relativeHumidity2M![i], - weatherCode: forecast.hourly!.weatherCode![i], - image: forecast.hourly!.isDay![i] == 1 - ? getDayImage( - forecast.hourly!.weatherCode![i].mapToWeatherState()) - : getNightImage( - forecast.hourly!.weatherCode![i].mapToWeatherState()), - daylightDuration: forecast.daily!.daylightDuration![0], - sunshineDuration: forecast.daily!.sunshineDuration![0], - sunrise: DateFormat('h : m a').format(sunrise!), - sunset: DateFormat('h : m a').format(sunset!), - uvIndexMax: forecast.daily!.uvIndexMax![0], - ), - ); - } - } else { - emit(const DetailedForecastError("Error processing forecast data")); - } - emit(DetailsForecastSuccess( - dailyForecast: dailyForecast, - hourlyForecast: hourlyForecast, - )); - }, - ); - } -} diff --git a/lib/features/daily_forecast/data/models/daily_forecast_model.dart b/lib/features/daily_forecast/data/models/daily_forecast_model.dart deleted file mode 100644 index 3347bfd..0000000 --- a/lib/features/daily_forecast/data/models/daily_forecast_model.dart +++ /dev/null @@ -1,19 +0,0 @@ -class DailyForecast { - String? time; - int? weatherCode; - String? sunrise; - String? sunset; - double? daylightDuration; - double? sunshineDuration; - double? uvIndexMax; - - DailyForecast({ - this.time, - this.weatherCode, - this.sunrise, - this.sunset, - this.daylightDuration, - this.sunshineDuration, - this.uvIndexMax, - }); -} diff --git a/lib/features/daily_forecast/data/models/daily_weather_theme.dart b/lib/features/daily_forecast/data/models/daily_weather_theme.dart deleted file mode 100644 index bab8cc0..0000000 --- a/lib/features/daily_forecast/data/models/daily_weather_theme.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'dart:ui'; - -import '../../../../core/global/enums.dart'; -import '../../../../core/utils/utils.dart'; - -class DailyWeatherTheme { - final String dayImage; - final String nightImage; - final Color textColor; - - DailyWeatherTheme({ - required this.dayImage, - required this.nightImage, - required this.textColor, - }); - factory DailyWeatherTheme.fromWeatherState(WeatherState state) { - switch (state) { - case WeatherState.Storm: - return DailyWeatherTheme( - dayImage: AppLottie.dailyStorm, - nightImage: AppLottie.dailyStorm, - textColor: AppColors.thunderstorm, - ); - case WeatherState.Rain: - return DailyWeatherTheme( - dayImage: AppLottie.dailyDayRain, - nightImage: AppLottie.dailyNightRain, - textColor: AppColors.rain, - ); - case WeatherState.Snow: - return DailyWeatherTheme( - dayImage: AppLottie.dailyDaySnow, - nightImage: AppLottie.dailyNightSnow, - textColor: AppColors.snow, - ); - case WeatherState.Wind: - return DailyWeatherTheme( - dayImage: AppLottie.dailyWind, - nightImage: AppLottie.dailyWind, - textColor: AppColors.wind, - ); - case WeatherState.Clear: - return DailyWeatherTheme( - dayImage: AppLottie.dailyDay, - nightImage: AppLottie.dailyNight, - textColor: AppColors.clear, - ); - case WeatherState.Clouds: - return DailyWeatherTheme( - dayImage: AppLottie.dailyDayCloud, - nightImage: AppLottie.dailyNightCloud, - textColor: AppColors.cloud, - ); - case WeatherState.Unknown: - // TODO: Add a custom image in case anything goes wrong. - throw 0; - } - } -} diff --git a/lib/features/daily_forecast/data/models/forecast_5_days_model.dart b/lib/features/daily_forecast/data/models/forecast_5_days_model.dart deleted file mode 100644 index 1261ec4..0000000 --- a/lib/features/daily_forecast/data/models/forecast_5_days_model.dart +++ /dev/null @@ -1,163 +0,0 @@ -class Forecast5DaysModel { - String cod; - int message; - int cnt; - List list; - - Forecast5DaysModel( - {required this.cod, - required this.message, - required this.cnt, - required this.list}); - - factory Forecast5DaysModel.fromJson(Map json) { - var list = json['list'] as List; - List weatherList = - list.map((i) => WeatherItem.fromJson(i)).toList(); - - return Forecast5DaysModel( - cod: json['cod'], - message: json['message'], - cnt: json['cnt'], - list: weatherList, - ); - } -} - -class WeatherItem { - int dt; - MainWeatherData main; - List weather; - Clouds clouds; - Wind wind; - int visibility; - double pop; - Sys sys; - String date; - - WeatherItem({ - required this.dt, - required this.main, - required this.weather, - required this.clouds, - required this.wind, - required this.visibility, - required this.pop, - required this.sys, - required this.date, - }); - - factory WeatherItem.fromJson(Map json) { - var weatherList = json['weather'] as List; - List weatherData = - weatherList.map((i) => WeatherDescription.fromJson(i)).toList(); - - return WeatherItem( - dt: json['dt'], - main: MainWeatherData.fromJson(json['main']), - weather: weatherData, - clouds: Clouds.fromJson(json['clouds']), - wind: Wind.fromJson(json['wind']), - visibility: json['visibility'], - pop: json['pop'].toDouble(), - sys: Sys.fromJson(json['sys']), - date: json['dt_txt'], - ); - } -} - -class MainWeatherData { - double temp; - double feelsLike; - double tempMin; - double tempMax; - int pressure; - int seaLevel; - int grndLevel; - int humidity; - double tempKf; - - MainWeatherData({ - required this.temp, - required this.feelsLike, - required this.tempMin, - required this.tempMax, - required this.pressure, - required this.seaLevel, - required this.grndLevel, - required this.humidity, - required this.tempKf, - }); - - factory MainWeatherData.fromJson(Map json) { - return MainWeatherData( - temp: json['temp'].toDouble(), - feelsLike: json['feels_like'].toDouble(), - tempMin: json['temp_min'].toDouble(), - tempMax: json['temp_max'].toDouble(), - pressure: json['pressure'], - seaLevel: json['sea_level'], - grndLevel: json['grnd_level'], - humidity: json['humidity'], - tempKf: json['temp_kf'].toDouble(), - ); - } -} - -class WeatherDescription { - int id; - String main; - String description; - String icon; - - WeatherDescription({ - required this.id, - required this.main, - required this.description, - required this.icon, - }); - - factory WeatherDescription.fromJson(Map json) { - return WeatherDescription( - id: json['id'], - main: json['main'], - description: json['description'], - icon: json['icon'], - ); - } -} - -class Clouds { - int all; - - Clouds({required this.all}); - - factory Clouds.fromJson(Map json) { - return Clouds(all: json['all']); - } -} - -class Wind { - double speed; - int deg; - double gust; - - Wind({required this.speed, required this.deg, required this.gust}); - - factory Wind.fromJson(Map json) { - return Wind( - speed: json['speed'].toDouble(), - deg: json['deg'], - gust: json['gust'].toDouble()); - } -} - -class Sys { - String pod; - - Sys({required this.pod}); - - factory Sys.fromJson(Map json) { - return Sys(pod: json['pod']); - } -} diff --git a/lib/features/daily_forecast/data/models/weather_data.dart b/lib/features/daily_forecast/data/models/weather_data.dart deleted file mode 100644 index 9c63acd..0000000 --- a/lib/features/daily_forecast/data/models/weather_data.dart +++ /dev/null @@ -1,97 +0,0 @@ -class OpenMeteoResponse { - Hourly? hourly; - Daily? daily; - - OpenMeteoResponse({ - this.hourly, - this.daily, - }); - - factory OpenMeteoResponse.fromJson(Map json) => - OpenMeteoResponse( - hourly: json["hourly"] == null ? null : Hourly.fromJson(json["hourly"]), - daily: json["daily"] == null ? null : Daily.fromJson(json["daily"]), - ); -} - -class Daily { - List? time; - List? weatherCode; - List? sunrise; - List? sunset; - List? daylightDuration; - List? sunshineDuration; - List? uvIndexMax; - - Daily({ - this.time, - this.weatherCode, - this.sunrise, - this.sunset, - this.daylightDuration, - this.sunshineDuration, - this.uvIndexMax, - }); - - factory Daily.fromJson(Map json) => Daily( - time: json["time"] == null - ? [] - : List.from(json["time"]!.map((x) => DateTime.parse(x))), - weatherCode: json["weather_code"] == null - ? [] - : List.from(json["weather_code"]!.map((x) => x)), - sunrise: json["sunrise"] == null - ? [] - : List.from(json["sunrise"]!.map((x) => x)), - sunset: json["sunset"] == null - ? [] - : List.from(json["sunset"]!.map((x) => x)), - daylightDuration: json["daylight_duration"] == null - ? [] - : List.from( - json["daylight_duration"]!.map((x) => x?.toDouble() ?? 0.0)), - sunshineDuration: json["sunshine_duration"] == null - ? [] - : List.from( - json["sunshine_duration"]!.map((x) => x?.toDouble() ?? 0.0)), - uvIndexMax: json["uv_index_max"] == null - ? [] - : List.from( - json["uv_index_max"]!.map((x) => x?.toDouble() ?? 0.0)), - ); -} - -class Hourly { - List? time; - List? temperature2M; - List? relativeHumidity2M; - List? weatherCode; - List? isDay; - - Hourly({ - this.time, - this.temperature2M, - this.relativeHumidity2M, - this.weatherCode, - this.isDay, - }); - - factory Hourly.fromJson(Map json) => Hourly( - time: json["time"] == null - ? [] - : List.from(json["time"]!.map((x) => x)), - temperature2M: json["temperature_2m"] == null - ? [] - : List.from( - json["temperature_2m"]!.map((x) => x?.toDouble())), - relativeHumidity2M: json["relative_humidity_2m"] == null - ? [] - : List.from(json["relative_humidity_2m"]!.map((x) => x)), - weatherCode: json["weather_code"] == null - ? [] - : List.from(json["weather_code"]!.map((x) => x)), - isDay: json["is_day"] == null - ? [] - : List.from(json["is_day"]!.map((x) => x)), - ); -} diff --git a/lib/features/daily_forecast/data/repo/detailed_forecast_repo.dart b/lib/features/daily_forecast/data/repo/detailed_forecast_repo.dart deleted file mode 100644 index 98704bf..0000000 --- a/lib/features/daily_forecast/data/repo/detailed_forecast_repo.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:clima/core/error/error_handling.dart'; -import 'package:fpdart/fpdart.dart'; - -import '../models/weather_data.dart'; - -abstract class DetailedForecastRepository { - Future> fetchWeatherData( - double? latitude, double? longitude); -} diff --git a/lib/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart b/lib/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart deleted file mode 100644 index a8e15df..0000000 --- a/lib/features/daily_forecast/data/repo/detailed_forecast_repo_impl.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:clima/core/error/error_handling.dart'; -import 'package:clima/features/daily_forecast/data/models/weather_data.dart'; -import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; -import 'package:dio/dio.dart'; -import 'package:fpdart/fpdart.dart'; - -class DetailedForecastRepoImpl extends DetailedForecastRepository { - @override - Future> fetchWeatherData( - double? latitude, double? longitude) async { - try { - var result = await Dio().get( - "https://api.open-meteo.com/v1/forecast?latitude=$latitude&longitude=$longitude¤t=temperature_2m,is_day,weather_code&hourly=temperature_2m,relative_humidity_2m,weather_code,is_day&daily=weather_code,sunrise,sunset,daylight_duration,sunshine_duration,uv_index_max&timezone=auto&forecast_days=16&forecast_hours=24"); - final data = OpenMeteoResponse.fromJson(result.data); - return right(data); - } catch (e) { - if (e is DioException) { - return left(ServerFailure.handel(e)); - } else { - return left(ServerFailure("ERROR :: ${e.toString()}")); - } - } - } -} diff --git a/lib/features/daily_forecast/screens/daily_forecast_screen.dart b/lib/features/daily_forecast/screens/daily_forecast_screen.dart index 8f3cea7..541e248 100644 --- a/lib/features/daily_forecast/screens/daily_forecast_screen.dart +++ b/lib/features/daily_forecast/screens/daily_forecast_screen.dart @@ -1,27 +1,11 @@ -import 'package:clima/core/common/failure_widget.dart'; -import 'package:clima/core/common/loading_widget.dart'; -import 'package:clima/features/daily_forecast/cubit/detailed_forecast_cubit.dart'; import 'package:clima/features/daily_forecast/screens/widgets/forecast_widget.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; class Forecast5DaysScreen extends StatelessWidget { const Forecast5DaysScreen({super.key}); @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is DetailedForecastInitial) { - return const LoadingWidget(); - } else if (state is DetailsForecastSuccess) { - return ForecastWidget(forecast: state.dailyForecast); - } else if (state is DetailedForecastError) { - return FailureWidget(text: state.errorMessage); - } else { - return const FailureWidget(); - } - }, - ); + return const ForecastWidget(); } } diff --git a/lib/features/daily_forecast/screens/widgets/daily_weather_list.dart b/lib/features/daily_forecast/screens/widgets/daily_weather_list.dart index c5482a2..e09dadb 100644 --- a/lib/features/daily_forecast/screens/widgets/daily_weather_list.dart +++ b/lib/features/daily_forecast/screens/widgets/daily_weather_list.dart @@ -1,15 +1,11 @@ import 'package:flutter/material.dart'; -import '../../data/models/daily_forecast_model.dart'; class DailyWeatherList extends StatelessWidget { const DailyWeatherList({ super.key, - required this.forecast, }); - final List forecast; - @override Widget build(BuildContext context) { return SliverList( @@ -19,7 +15,7 @@ class DailyWeatherList extends StatelessWidget { child: Text("No Data Yet"), ); }, - childCount: forecast.length, + childCount: 7, ), ); } diff --git a/lib/features/daily_forecast/screens/widgets/forecast_widget.dart b/lib/features/daily_forecast/screens/widgets/forecast_widget.dart index 7775244..89be16e 100644 --- a/lib/features/daily_forecast/screens/widgets/forecast_widget.dart +++ b/lib/features/daily_forecast/screens/widgets/forecast_widget.dart @@ -1,14 +1,10 @@ import 'package:flutter/material.dart'; -import '../../data/models/daily_forecast_model.dart'; -import 'daily_weather_list.dart'; - class ForecastWidget extends StatelessWidget { const ForecastWidget({ super.key, - required this.forecast, }); - final List forecast; + @override Widget build(BuildContext context) { return CustomScrollView( @@ -17,7 +13,6 @@ class ForecastWidget extends StatelessWidget { title: Text("Weak Forecast"), pinned: true, ), - DailyWeatherList(forecast: forecast), ], ); } diff --git a/lib/features/home/screens/widgets/weather_status.dart b/lib/features/home/screens/widgets/weather_status.dart index 5ef81c6..60b7538 100644 --- a/lib/features/home/screens/widgets/weather_status.dart +++ b/lib/features/home/screens/widgets/weather_status.dart @@ -17,7 +17,7 @@ class WeatherStatus extends StatelessWidget { children: [ TemperatureText(temperature: temperature), Text(weatherState!, - style: AppTypography.bold24( + style: AppTypography.bold28( color: textColor, )), ], diff --git a/lib/features/hourly_forecast/cubit/hourly_forecast_cubit.dart b/lib/features/hourly_forecast/cubit/hourly_forecast_cubit.dart new file mode 100644 index 0000000..981b649 --- /dev/null +++ b/lib/features/hourly_forecast/cubit/hourly_forecast_cubit.dart @@ -0,0 +1,78 @@ +import 'package:clima/core/extensions/map_weather_code.dart'; +import 'package:clima/core/helper/location_helper.dart'; +import 'package:clima/features/hourly_forecast/data/repo/hourly_forecast_repo.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; + +import '../../../core/helper/functions.dart'; +import '../../home/data/model/weather_theme.dart'; +import '../data/models/weather_daily_model.dart'; +import '../data/models/weather_hourly_model.dart'; + +part 'hourly_forecast_state.dart'; + +class HourlyForecastCubit extends Cubit { + HourlyForecastCubit(this._repository) : super(DetailedForecastInitial()); + final HourlyForecastRepository _repository; + final List _propertiesToCheck = [ + 'hourly.time', + 'hourly.temperature2m', + 'daily.time', + 'daily.temperature2mMax', + 'daily.temperature2mMin', + ]; + + fetchWeatherData() async { + var result = await _repository.fetchWeatherData( + Location.instance.position?.latitude, + Location.instance.position?.longitude); + result.fold( + (failure) => emit(DetailedForecastError(failure.message)), + (response) { + // To make sure there is no null values + if (_propertiesToCheck.any((property) => isNull(response, property))) { + emit(const DetailedForecastError( + "Invalid or incomplete response data")); + } + emit( + DetailsForecastSuccess( + dailyForecast: Daily( + theme: WeatherTheme.mapWeatherStateToTheme( + response.hourly.weatherCode[0].mapToWeatherState(), + response.hourly.isDay[0] == 1 ? true : false), + date: DateFormat('EEEE, d MMMM') + .format(DateTime.parse(response.daily.time[0])), + sunrise: DateFormat('h:mm a') + .format(DateTime.parse(response.daily.sunrise[0])), + sunshineDuration: response.daily.sunshineDuration[0], + daylightDuration: response.daily.daylightDuration[0], + temperatureMax: response.daily.temperature2mMax[0], + temperatureMin: response.daily.temperature2mMin[0], + uvIndexMax: response.daily.uvIndexMax[0], + sunset: DateFormat('h:mm a') + .format(DateTime.parse(response.daily.sunset[0])), + apparentTemperature: (response.daily.apparentTemperatureMax[0] + + response.daily.apparentTemperatureMin[0]) / + 2), + hourlyForecast: WeatherHourly( + time: response.hourly.time + .map((date) => + DateFormat('h a').format(DateTime.parse(date))) + .toList(), + isDay: response.hourly.isDay, + weatherCode: response.hourly.weatherCode, + humidity: response.hourly.humidity, + temperature: response.hourly.temperature, + image: List.generate( + response.hourly.weatherCode.length, + (index) => response.hourly.weatherCode[index] + .mapToWeatherState() + .getImages(response.hourly.isDay[index] == 1), + )), + ), + ); + }, + ); + } +} diff --git a/lib/features/daily_forecast/cubit/detailed_forecast_state.dart b/lib/features/hourly_forecast/cubit/hourly_forecast_state.dart similarity index 82% rename from lib/features/daily_forecast/cubit/detailed_forecast_state.dart rename to lib/features/hourly_forecast/cubit/hourly_forecast_state.dart index 452faea..61bd319 100644 --- a/lib/features/daily_forecast/cubit/detailed_forecast_state.dart +++ b/lib/features/hourly_forecast/cubit/hourly_forecast_state.dart @@ -1,4 +1,4 @@ -part of 'detailed_forecast_cubit.dart'; +part of 'hourly_forecast_cubit.dart'; abstract class DetailedForecastState extends Equatable { const DetailedForecastState(); @@ -10,8 +10,8 @@ abstract class DetailedForecastState extends Equatable { class DetailedForecastInitial extends DetailedForecastState {} class DetailsForecastSuccess extends DetailedForecastState { - final List dailyForecast; - final List hourlyForecast; + final Daily dailyForecast; + final WeatherHourly hourlyForecast; const DetailsForecastSuccess( {required this.dailyForecast, required this.hourlyForecast}); } diff --git a/lib/features/hourly_forecast/data/models/hourly_forecast_model.dart b/lib/features/hourly_forecast/data/models/hourly_forecast_model.dart deleted file mode 100644 index 2c7200c..0000000 --- a/lib/features/hourly_forecast/data/models/hourly_forecast_model.dart +++ /dev/null @@ -1,27 +0,0 @@ -class HourlyForecast { - String? todayDate; - String? hour; - double? temperature; - int? humidity; - int? weatherCode; - String? image; - double? daylightDuration; - double? sunshineDuration; - String? sunrise; - String? sunset; - double? uvIndexMax; - - HourlyForecast({ - this.todayDate, - this.hour, - this.temperature, - this.humidity, - this.weatherCode, - this.image, - this.daylightDuration, - this.sunshineDuration, - this.sunrise, - this.sunset, - this.uvIndexMax, - }); -} diff --git a/lib/features/hourly_forecast/data/models/open_meteo_hourly_response_model.dart b/lib/features/hourly_forecast/data/models/open_meteo_hourly_response_model.dart new file mode 100644 index 0000000..6de7d7f --- /dev/null +++ b/lib/features/hourly_forecast/data/models/open_meteo_hourly_response_model.dart @@ -0,0 +1,18 @@ +import 'package:clima/features/hourly_forecast/data/models/weather_daily_model.dart'; +import 'package:clima/features/hourly_forecast/data/models/weather_hourly_model.dart'; + +class OpenMeteoHourlyResponse { + WeatherHourly hourly; + WeatherDaily daily; + + OpenMeteoHourlyResponse({ + required this.hourly, + required this.daily, + }); + + factory OpenMeteoHourlyResponse.fromJson(Map json) => + OpenMeteoHourlyResponse( + hourly: WeatherHourly.fromJson(json["hourly"]), + daily: WeatherDaily.fromJson(json["daily"]), + ); +} diff --git a/lib/features/hourly_forecast/data/models/weather_daily_model.dart b/lib/features/hourly_forecast/data/models/weather_daily_model.dart new file mode 100644 index 0000000..ee650bc --- /dev/null +++ b/lib/features/hourly_forecast/data/models/weather_daily_model.dart @@ -0,0 +1,69 @@ +import 'package:clima/features/home/data/model/weather_theme.dart'; + +class WeatherDaily { + final List time; + final List temperature2mMax; + final List temperature2mMin; + final List apparentTemperatureMax; + final List apparentTemperatureMin; + final List sunrise; + final List sunset; + final List daylightDuration; + final List sunshineDuration; + final List uvIndexMax; + + WeatherDaily({ + required this.time, + required this.temperature2mMax, + required this.temperature2mMin, + required this.apparentTemperatureMax, + required this.apparentTemperatureMin, + required this.sunrise, + required this.sunset, + required this.daylightDuration, + required this.sunshineDuration, + required this.uvIndexMax, + }); + + factory WeatherDaily.fromJson(Map json) { + // apparent_temperature_max,apparent_temperature_min, + return WeatherDaily( + time: List.from(json['time']), + temperature2mMax: List.from(json['temperature_2m_max']), + temperature2mMin: List.from(json['temperature_2m_min']), + apparentTemperatureMax: List.from(json['apparent_temperature_max']), + apparentTemperatureMin: List.from(json['apparent_temperature_min']), + sunrise: List.from(json['sunrise']), + sunset: List.from(json['sunset']), + daylightDuration: List.from(json['daylight_duration']), + sunshineDuration: List.from(json['sunshine_duration']), + uvIndexMax: List.from(json['uv_index_max']), + ); + } +} + +class Daily { + final String date; + final num temperatureMax; + final num temperatureMin; + final num apparentTemperature; + final String sunrise; + final String sunset; + final num daylightDuration; + final num sunshineDuration; + final num uvIndexMax; + final WeatherTheme theme; + + Daily({ + required this.date, + required this.temperatureMax, + required this.temperatureMin, + required this.apparentTemperature, + required this.sunrise, + required this.sunset, + required this.daylightDuration, + required this.sunshineDuration, + required this.uvIndexMax, + required this.theme, + }); +} diff --git a/lib/features/hourly_forecast/data/models/weather_hourly_model.dart b/lib/features/hourly_forecast/data/models/weather_hourly_model.dart new file mode 100644 index 0000000..7b12e57 --- /dev/null +++ b/lib/features/hourly_forecast/data/models/weather_hourly_model.dart @@ -0,0 +1,29 @@ +class WeatherHourly { + final List time; + final List temperature; + final List humidity; + final List isDay; + final List weatherCode; + + List image; + + WeatherHourly({ + required this.time, + required this.temperature, + required this.humidity, + required this.isDay, + required this.weatherCode, + required this.image, + }); + + factory WeatherHourly.fromJson(Map json) { + return WeatherHourly( + time: List.from(json['time']), + temperature: List.from(json['temperature_2m']), + humidity: List.from(json['relative_humidity_2m']), + isDay: List.from(json['is_day']), + weatherCode: List.from(json['weather_code']), + image: [], + ); + } +} diff --git a/lib/features/hourly_forecast/data/repo/hourly_forecast_repo.dart b/lib/features/hourly_forecast/data/repo/hourly_forecast_repo.dart new file mode 100644 index 0000000..b456a35 --- /dev/null +++ b/lib/features/hourly_forecast/data/repo/hourly_forecast_repo.dart @@ -0,0 +1,9 @@ +import 'package:clima/core/error/error_handling.dart'; +import 'package:fpdart/fpdart.dart'; + +import '../models/open_meteo_hourly_response_model.dart'; + +abstract class HourlyForecastRepository { + Future> fetchWeatherData( + double? latitude, double? longitude); +} diff --git a/lib/features/hourly_forecast/data/repo/hourly_forecast_repo_impl.dart b/lib/features/hourly_forecast/data/repo/hourly_forecast_repo_impl.dart new file mode 100644 index 0000000..a882d64 --- /dev/null +++ b/lib/features/hourly_forecast/data/repo/hourly_forecast_repo_impl.dart @@ -0,0 +1,26 @@ +import 'package:clima/core/error/error_handling.dart'; +import 'package:dio/dio.dart'; +import 'package:fpdart/fpdart.dart'; + +import '../models/open_meteo_hourly_response_model.dart'; +import 'hourly_forecast_repo.dart'; + +class HourlyForecastRepoImpl extends HourlyForecastRepository { + @override + Future> fetchWeatherData( + double? latitude, double? longitude) async { + try { + var result = await Dio().get( + "https://api.open-meteo.com/v1/forecast?latitude=$latitude&longitude=$longitude&hourly=temperature_2m,relative_humidity_2m,weather_code,is_day&daily=weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,sunrise,sunset,daylight_duration,sunshine_duration,uv_index_max&timezone=auto&forecast_days=1&forecast_hours=24"); + final data = OpenMeteoHourlyResponse.fromJson(result.data); + print(data.daily.time); + return right(data); + } catch (e) { + if (e is DioException) { + return left(ServerFailure.handel(e)); + } else { + return left(ServerFailure("ERROR :: ${e.toString()}")); + } + } + } +} diff --git a/lib/features/hourly_forecast/screens/hourly_forecast_screen.dart b/lib/features/hourly_forecast/screens/hourly_forecast_screen.dart index 80c9028..0e9b6eb 100644 --- a/lib/features/hourly_forecast/screens/hourly_forecast_screen.dart +++ b/lib/features/hourly_forecast/screens/hourly_forecast_screen.dart @@ -4,19 +4,22 @@ import 'package:clima/features/hourly_forecast/screens/widgets/hourly_forecast_w import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../daily_forecast/cubit/detailed_forecast_cubit.dart'; +import '../cubit/hourly_forecast_cubit.dart'; class HourlyForecastScreen extends StatelessWidget { const HourlyForecastScreen({super.key}); @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { if (state is DetailedForecastInitial) { return const LoadingWidget(); } else if (state is DetailsForecastSuccess) { - return HourlyForecastWidget(forecast: state.hourlyForecast); + return HourlyForecastWidget( + hourlyForecast: state.hourlyForecast, + dailyForecast: state.dailyForecast, + ); } else if (state is DetailedForecastError) { return FailureWidget(text: state.errorMessage); } else { diff --git a/lib/features/hourly_forecast/screens/widgets/day_and_night_widget.dart b/lib/features/hourly_forecast/screens/widgets/day_and_night_widget.dart new file mode 100644 index 0000000..4cd65a4 --- /dev/null +++ b/lib/features/hourly_forecast/screens/widgets/day_and_night_widget.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +import '../../../../core/utils/utils.dart'; + +class DayAndNight extends StatelessWidget { + const DayAndNight({ + super.key, + required this.sunset, + required this.sunrise, + }); + final String sunset; + final String sunrise; + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric(horizontal: AppDimensions.width! * 0.03), + padding: EdgeInsets.all(AppDimensions.width! * 0.035), + decoration: AppDecoration.container(context), + width: AppDimensions.width, + height: AppDimensions.height! * 0.25, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Sunrise", style: AppTypography.medium18()), + const SizedBox(height: 5), + Text(sunrise), + const SizedBox(height: 5), + Expanded( + child: Image.asset( + AppImages.daySun, + fit: BoxFit.cover, + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Sunset", style: AppTypography.medium18()), + const SizedBox(height: 5), + Text(sunset), + const SizedBox(height: 5), + Expanded( + child: Image.asset( + AppImages.nightMoon, + fit: BoxFit.cover, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart index 287719b..0ed780d 100644 --- a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart +++ b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_details.dart @@ -3,23 +3,23 @@ import 'package:clima/core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:lottie/lottie.dart'; -import '../../data/models/hourly_forecast_model.dart'; +import '../../data/models/weather_hourly_model.dart'; class HourlyForecastDetails extends StatelessWidget { const HourlyForecastDetails({ super.key, - required this.forecastList, + required this.hourlyForecast, }); - final List forecastList; + final WeatherHourly hourlyForecast; @override Widget build(BuildContext context) { return SizedBox( height: AppDimensions.height! * 0.2, child: ListView.separated( - itemCount: forecastList.length, - padding: const EdgeInsets.only(left: 16), + itemCount: hourlyForecast.time.length, + padding: const EdgeInsets.symmetric(horizontal: 16), scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), itemBuilder: (context, index) => Container( @@ -29,18 +29,18 @@ class HourlyForecastDetails extends StatelessWidget { child: Column( children: [ Expanded( - child: Text(forecastList[index].hour!), + child: Text(hourlyForecast.time[index]), ), Expanded( flex: 3, child: RepaintBoundary( - child: Lottie.asset(forecastList[index].image!), + child: Lottie.asset(hourlyForecast.image[index]), ), ), const SizedBox(height: 8), Expanded( child: Text( - forecastList[index].temperature!.toString(), + hourlyForecast.temperature[index].toString(), style: AppTypography.medium14(), ), ), @@ -49,7 +49,7 @@ class HourlyForecastDetails extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.water_drop_outlined, size: 16), - Text(forecastList[index].humidity.toString()), + Text(hourlyForecast.humidity[index].toString()), ], ), ), diff --git a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart index c85c2d3..8501ba3 100644 --- a/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart +++ b/lib/features/hourly_forecast/screens/widgets/hourly_forecast_widget.dart @@ -1,14 +1,21 @@ -import 'package:clima/core/utils/app_decoration.dart'; +import 'package:clima/core/helper/location_helper.dart'; import 'package:clima/core/utils/app_dimn.dart'; +import 'package:clima/core/utils/app_typography.dart'; +import 'package:clima/features/home/screens/widgets/widgets.dart'; import 'package:flutter/material.dart'; -import '../../data/models/hourly_forecast_model.dart'; +import '../../../../core/common/temperature_text.dart'; +import '../../data/models/weather_daily_model.dart'; +import '../../data/models/weather_hourly_model.dart'; +import 'day_and_night_widget.dart'; import 'hourly_forecast_details.dart'; class HourlyForecastWidget extends StatefulWidget { - const HourlyForecastWidget({Key? key, required this.forecast}) + const HourlyForecastWidget( + {Key? key, required this.hourlyForecast, required this.dailyForecast}) : super(key: key); - final List forecast; + final WeatherHourly hourlyForecast; + final Daily dailyForecast; @override State createState() => _HourlyForecastWidgetState(); @@ -21,19 +28,27 @@ class _HourlyForecastWidgetState extends State { appBar: AppBar( title: const Text("Today's details"), actions: [ - Text(widget.forecast[0].todayDate!), - const SizedBox(width: 8), + Text(widget.dailyForecast.date), + const SizedBox(width: 16), ], ), body: SingleChildScrollView( child: Column( children: [ - SizedBox(height: AppDimensions.height! * 0.005), - HourlyForecastDetails(forecastList: widget.forecast), + SizedBox(height: AppDimensions.height! * 0.01), + Padding( + padding: const EdgeInsets.all(16), + child: SizedBox( + height: AppDimensions.height! * 0.27, + child: HourlyTopSection(widget: widget), + ), + ), + SizedBox(height: AppDimensions.height! * 0.02), + HourlyForecastDetails(hourlyForecast: widget.hourlyForecast), SizedBox(height: AppDimensions.height! * 0.02), DayAndNight( - sunset: widget.forecast[0].sunset!, - sunrise: widget.forecast[0].sunrise!, + sunset: widget.dailyForecast.sunset, + sunrise: widget.dailyForecast.sunrise, ), ], ), @@ -42,42 +57,58 @@ class _HourlyForecastWidgetState extends State { } } -class DayAndNight extends StatelessWidget { - const DayAndNight({ +class HourlyTopSection extends StatelessWidget { + const HourlyTopSection({ super.key, - required this.sunset, - required this.sunrise, + required this.widget, }); - final String sunset; - final String sunrise; + + final HourlyForecastWidget widget; + @override Widget build(BuildContext context) { - return Container( - margin: EdgeInsets.symmetric(horizontal: AppDimensions.width! * 0.03), - decoration: AppDecoration.container(context), - width: AppDimensions.width, - height: AppDimensions.height! * 0.25, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(sunrise), - ], - ), + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: WeatherImage( + image: widget.dailyForecast.theme.image, + begin: -30, + end: 5, + isCenter: false, + ), + ), + const SizedBox(height: 16), + Text( + "${widget.dailyForecast.temperatureMin} / ${widget.dailyForecast.temperatureMax} feels like ${widget.dailyForecast.apparentTemperature}", + ), + ], ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(sunset), - ], - ), + ), + Expanded( + child: Column( + children: [ + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + child: TemperatureText( + temperature: + widget.hourlyForecast.temperature[0].toString()), + ), + ), + Text(Location.instance.city, style: AppTypography.bold24()), + Text( + Location.instance.country, + style: AppTypography.thin14(), + ), + ], ), - ], - ), + ), + ], ); } } diff --git a/lib/features/landing_page/landing_screen.dart b/lib/features/landing_page/landing_screen.dart index 58e603b..49763ed 100644 --- a/lib/features/landing_page/landing_screen.dart +++ b/lib/features/landing_page/landing_screen.dart @@ -1,8 +1,7 @@ import 'package:clima/core/services/get_it_service.dart'; -import 'package:clima/features/daily_forecast/cubit/detailed_forecast_cubit.dart'; -import 'package:clima/features/daily_forecast/data/repo/detailed_forecast_repo.dart'; import 'package:clima/features/home/cubit/home_cubit.dart'; import 'package:clima/features/home/data/repo/home_repo.dart'; +import 'package:clima/features/hourly_forecast/data/repo/hourly_forecast_repo.dart'; import 'package:clima/features/landing_page/widgets/bottom_nav_bar_list.dart'; import 'package:clima/features/landing_page/widgets/location_service_disabled.dart'; import 'package:clima/features/landing_page/widgets/permission_denied_widget.dart'; @@ -11,6 +10,7 @@ import 'package:clima/features/landing_page/widgets/waiting_permission_widget.da import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import '../hourly_forecast/cubit/hourly_forecast_cubit.dart'; import 'bloc/location/location_bloc.dart'; import 'bloc/nav_bar/nav_bar_bloc.dart'; @@ -33,7 +33,7 @@ class LandingScreen extends StatelessWidget { ), BlocProvider( create: (context) => - DetailedForecastCubit(getIt.get()) + HourlyForecastCubit(getIt.get()) ..fetchWeatherData(), ), ], @@ -85,7 +85,7 @@ class LandingScreen extends StatelessWidget { } void fetchData(BuildContext context) async { - BlocProvider.of(context).fetchWeatherData(); + BlocProvider.of(context).fetchWeatherData(); BlocProvider.of(context).fetchWeatherData(); } } diff --git a/pubspec.lock b/pubspec.lock index bc526e6..d69680d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -153,19 +153,19 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" - fl_chart: - dependency: "direct main" - description: - name: fl_chart - sha256: "6b9eb2b3017241d05c482c01f668dd05cc909ec9a0114fdd49acd958ff2432fa" - url: "https://pub.dev" - source: hosted - version: "0.64.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_animate: + dependency: "direct main" + description: + name: flutter_animate + sha256: "1dbc1aabfb8ec1e9d9feed2b675c21fb6b0a11f99be53ec3bc0f1901af6a8eb7" + url: "https://pub.dev" + source: hosted + version: "4.3.0" flutter_bloc: dependency: "direct main" description: @@ -384,14 +384,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.15.4" - http: - dependency: "direct main" - description: - name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" - url: "https://pub.dev" - source: hosted - version: "1.1.0" http_parser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7a8ea57..3b22478 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,17 +17,16 @@ dependencies: flutter_bloc: ^8.1.3 get_it: ^7.6.4 lottie: ^2.7.0 - http: iconsax: ^0.0.8 flutter_local_notifications: ^16.1.0 path_provider: ^2.1.1 - fl_chart: ^0.64.0 equatable: ^2.0.5 flutter_svg: ^2.0.9 connectivity_plus: ^5.0.1 geocoding: ^2.1.1 google_maps_flutter: ^2.5.0 intl: + flutter_animate: ^4.3.0 dev_dependencies: flutter_test: