diff --git a/lib/Models/WeatherData.dart b/lib/Models/WeatherData.dart new file mode 100644 index 0000000..dc112ab --- /dev/null +++ b/lib/Models/WeatherData.dart @@ -0,0 +1,217 @@ +class WeatherData { + String? cod; + int? message; + int? cnt; + List? list; + City? city; + + WeatherData({this.cod, this.message, this.cnt, this.list, this.city}); + + factory WeatherData.fromJson(Map json) { + return WeatherData( + cod: json['cod'], + message: json['message'], + cnt: json['cnt'], + list: (json['list'] as List?)?.map((item) => WeatherForecast.fromJson(item)).toList(), + city: json['city'] != null ? City.fromJson(json['city']) : null, + ); + } +} + +class WeatherForecast { + int? dt; + MainWeatherData? main; + List? weather; + Clouds? clouds; + Wind? wind; + int? visibility; + dynamic pop; + Rain? rain; + Sys? sys; + String? dtTxt; + + WeatherForecast({ + this.dt, + this.main, + this.weather, + this.clouds, + this.wind, + this.visibility, + this.pop, + this.rain, + this.sys, + this.dtTxt, + }); + + factory WeatherForecast.fromJson(Map json) { + return WeatherForecast( + dt: json['dt'], + main: json['main'] != null ? MainWeatherData.fromJson(json['main']) : null, + weather: (json['weather'] as List?)?.map((item) => Weather.fromJson(item)).toList(), + clouds: json['clouds'] != null ? Clouds.fromJson(json['clouds']) : null, + wind: json['wind'] != null ? Wind.fromJson(json['wind']) : null, + visibility: json['visibility'], + pop: json['pop'], + rain: json['rain'] != null ? Rain.fromJson(json['rain']) : null, + sys: json['sys'] != null ? Sys.fromJson(json['sys']) : null, + dtTxt: json['dt_txt'], + ); + } +} + +class MainWeatherData { + double? temp; + double? feelsLike; + double? tempMin; + double? tempMax; + int? pressure; + int? seaLevel; + int? grndLevel; + int? humidity; + double? tempKf; + + MainWeatherData({ + this.temp, + this.feelsLike, + this.tempMin, + this.tempMax, + this.pressure, + this.seaLevel, + this.grndLevel, + this.humidity, + 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 Weather { + int? id; + String? main; + String? description; + String? icon; + + Weather({this.id, this.main, this.description, this.icon}); + + factory Weather.fromJson(Map json) { + return Weather( + id: json['id'], + main: json['main'], + description: json['description'], + icon: json['icon'], + ); + } +} + +class Clouds { + int? all; + + Clouds({this.all}); + + factory Clouds.fromJson(Map json) { + return Clouds( + all: json['all'], + ); + } +} + +class Wind { + double? speed; + int? deg; + double? gust; + + Wind({this.speed, this.deg, this.gust}); + + factory Wind.fromJson(Map json) { + return Wind( + speed: json['speed']?.toDouble(), + deg: json['deg'], + gust: json['gust']?.toDouble(), + ); + } +} + +class Rain { + double? h3; + + Rain({this.h3}); + + factory Rain.fromJson(Map json) { + return Rain( + h3: json['3h']?.toDouble(), + ); + } +} + +class Sys { + String? pod; + + Sys({this.pod}); + + factory Sys.fromJson(Map json) { + return Sys( + pod: json['pod'], + ); + } +} + +class City { + int? id; + String? name; + Coord? coord; + String? country; + int? population; + int? timezone; + int? sunrise; + int? sunset; + + City({ + this.id, + this.name, + this.coord, + this.country, + this.population, + this.timezone, + this.sunrise, + this.sunset, + }); + + factory City.fromJson(Map json) { + return City( + id: json['id'], + name: json['name'], + coord: json['coord'] != null ? Coord.fromJson(json['coord']) : null, + country: json['country'], + population: json['population'], + timezone: json['timezone'], + sunrise: json['sunrise'], + sunset: json['sunset'], + ); + } +} + +class Coord { + double? lat; + double? lon; + + Coord({this.lat, this.lon}); + + factory Coord.fromJson(Map json) { + return Coord( + lat: json['lat']?.toDouble(), + lon: json['lon']?.toDouble(), + ); + } +} diff --git a/lib/Screens/MainView/main_view.dart b/lib/Screens/MainView/main_view.dart index 9ec960a..5b1f3ff 100644 --- a/lib/Screens/MainView/main_view.dart +++ b/lib/Screens/MainView/main_view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'dart:math'; @@ -15,9 +16,11 @@ import 'package:tablet_app/Components/loading_common.dart'; import 'package:tablet_app/Helpers/DatabaseHelper.dart'; import 'package:tablet_app/Helpers/ImageCustomProvider.dart'; import 'package:tablet_app/Helpers/MQTTHelper.dart'; +import 'package:tablet_app/Models/WeatherData.dart'; import 'package:tablet_app/Screens/Agenda/agenda_view.dart'; import 'package:tablet_app/Screens/Article/article_view.dart'; import 'package:tablet_app/Screens/Configuration/config_view.dart'; +import 'package:tablet_app/Screens/MainView/weather_view.dart'; import 'package:tablet_app/Screens/Map/map_context.dart'; import 'package:tablet_app/Screens/Map/map_view.dart'; import 'package:tablet_app/Models/map-marker.dart'; @@ -324,14 +327,27 @@ class _MainViewWidget extends State { ) : getGridSections(appContext), ), ), - if(configurationDTO.weatherCity != null && configurationDTO.weatherCity!.length > 2) + if(configurationDTO.weatherCity != null && configurationDTO.weatherCity!.length > 2 && configurationDTO.weatherResult != null) Positioned( bottom: 0, left: 0, child: Padding( padding: const EdgeInsets.all(8.0), - child: Text('Weather - ' + configurationDTO.weatherCity!, style: TextStyle(fontSize: 15, color: textColor)) - )) + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.0)), + color: kBackgroundGrey + ), + child: Builder( + builder: (context) { + Map weatherResultInJson = jsonDecode(configurationDTO.weatherResult!); + + WeatherData weatherDataResult = WeatherData.fromJson(weatherResultInJson); + return WeatherView(weatherData: weatherDataResult); + } + ), + ), + )) ]), ), ); @@ -393,9 +409,6 @@ class _MainViewWidget extends State { actions: [], ), context: context ); - print("COUCOUCOUCOU"); - print(result); - isDialogOpen = false; } else { print("ALREADY OPEN LAAAA"); @@ -422,7 +435,7 @@ class _MainViewWidget extends State { return ConfigViewWidget(); }, ), - (Route route) => false // For pushAndRemoveUntil + (Route route) => false // For pushAndRemoveUntil ); } } diff --git a/lib/Screens/MainView/weather_view.dart b/lib/Screens/MainView/weather_view.dart new file mode 100644 index 0000000..42d64c2 --- /dev/null +++ b/lib/Screens/MainView/weather_view.dart @@ -0,0 +1,294 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tablet_app/Models/WeatherData.dart'; +import 'package:tablet_app/app_context.dart'; +import 'package:tablet_app/constants.dart'; +import 'package:intl/intl.dart'; + +class WeatherView extends StatelessWidget { + final WeatherData weatherData; + WeatherView({required this.weatherData}); + + int nbrNextHours = 5; + + String formatTimestamp(int timestamp, AppContext appContext, bool isHourOnly, bool isDateOnly) { + + DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); + + // Determine the date format based on the application language + String dateFormat = appContext.getContext().language.toString().toUpperCase() == "EN" ? + 'MM/dd/yyyy HH:mm' + : 'dd/MM/yyyy HH:mm'; + + if(isHourOnly) { + dateFormat = 'HH:mm'; + } + + if(isDateOnly) { + dateFormat = dateFormat.replaceAll(' HH:mm', ''); + } + + String formattedDate = DateFormat(dateFormat).format(dateTime); + + return formattedDate; + } + + List getNextFiveDaysForecast(List allForecasts) { + List nextFiveDaysForecast = []; + DateTime today = DateTime.now(); + + List nextDay1All = allForecasts.where((af) => (DateTime.fromMillisecondsSinceEpoch(af.dt! * 1000)).day == (today.add(Duration(days: 1))).day).toList(); + List nextDay2All = allForecasts.where((af) => (DateTime.fromMillisecondsSinceEpoch(af.dt! * 1000)).day == (today.add(Duration(days: 2))).day).toList(); + List nextDay3All = allForecasts.where((af) => (DateTime.fromMillisecondsSinceEpoch(af.dt! * 1000)).day == (today.add(Duration(days: 3))).day).toList(); + List nextDay4All = allForecasts.where((af) => (DateTime.fromMillisecondsSinceEpoch(af.dt! * 1000)).day == (today.add(Duration(days: 4))).day).toList(); + List nextDay5All = allForecasts.where((af) => (DateTime.fromMillisecondsSinceEpoch(af.dt! * 1000)).day == (today.add(Duration(days: 5))).day).toList(); + + var nextDay1MiddayTest = nextDay1All.where((nd) => (DateTime.fromMillisecondsSinceEpoch(nd.dt! * 1000)).hour == 12).firstOrNull; + WeatherForecast nextDay1AllSummary = nextDay1MiddayTest != null ? nextDay1MiddayTest : nextDay1All.last; + nextFiveDaysForecast.add(nextDay1AllSummary); + + var nextDay2MiddayTest = nextDay2All.where((nd) => (DateTime.fromMillisecondsSinceEpoch(nd.dt! * 1000)).hour == 12).firstOrNull; + WeatherForecast nextDay2Midday = nextDay2MiddayTest != null ? nextDay2MiddayTest : nextDay2All.last; + nextFiveDaysForecast.add(nextDay2Midday); + + var nextDay3MiddayTest = nextDay3All.where((nd) => (DateTime.fromMillisecondsSinceEpoch(nd.dt! * 1000)).hour == 12).firstOrNull; + WeatherForecast nextDay3Midday = nextDay3MiddayTest != null ? nextDay3MiddayTest : nextDay3All.last; + nextFiveDaysForecast.add(nextDay3Midday); + + var nextDay4MiddayTest = nextDay4All.where((nd) => (DateTime.fromMillisecondsSinceEpoch(nd.dt! * 1000)).hour == 12).firstOrNull; + WeatherForecast nextDay4Midday = nextDay4MiddayTest != null ? nextDay4MiddayTest : nextDay4All.last; + nextFiveDaysForecast.add(nextDay4Midday); + + var nextDay5MiddayTest = nextDay5All.where((nd) => (DateTime.fromMillisecondsSinceEpoch(nd.dt! * 1000)).hour == 12).firstOrNull; + WeatherForecast nextDay5Midday = nextDay5MiddayTest != null ? nextDay5MiddayTest : nextDay5All.last; + nextFiveDaysForecast.add(nextDay5Midday); + + return nextFiveDaysForecast; + } + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + final appContext = Provider.of(context); + + return InkWell( + onTap: () { + + print(weatherData.list!.first.weather!.first.main); + print(weatherData.list!.first.weather!.first.description); + print(weatherData.list!.first.weather!.first.icon!); + print(weatherData.list!.first.dtTxt); + print(weatherData.list!.first.dt); + + String formattedDate = formatTimestamp(weatherData.list!.first.dt!, appContext, false, false); + + getNextFiveDaysForecast(weatherData.list!); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20.0)) + ), + contentPadding: EdgeInsets.zero, + // title: Text(eventAgenda.name!), + content: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.0)), + color: kBackgroundLight, + ), + height: size.height * 0.9, + width: size.width * 0.7, + child: Stack( + children: [ + Positioned( + right: 5, + top: 5, + child: InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: kMainGrey, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: kMainGrey, + spreadRadius: 0.5, + blurRadius: 1.1, + offset: Offset(0, 1.1), // changes position of shadow + ), + ], + ), + child: Icon( + Icons.close, + size: 25, + color: Colors.white, + ), + ), + ), + ), + SizedBox( + //height: 300, + //width: 300, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center(child: Text(weatherData.city!.name!, style: TextStyle(fontSize: kSectionTitleDetailSize, fontWeight: FontWeight.w400),)), + ), + SizedBox( + height: 125, + width: 125, + child: Container( + color: Colors.red, child: Center(child: CachedNetworkImage(imageUrl: "https://openweathermap.org/img/wn/${weatherData.list!.first.weather!.first.icon!}@4x.png"))), + ), + /*Center( + child: Text("${formattedDate}", style: TextStyle(fontSize: kSectionDescriptionDetailSize, fontWeight: FontWeight.w400)) + ),*/ + Container( + color: Colors.green, + width: size.width * 0.4, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Icon(Icons.water_drop_outlined), + Text("${weatherData.list!.first.pop!.round().toString()}%", style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w400)), + ], + ), + ), + Text("${weatherData.list!.first.main!.temp!.round().toString()}°", style: TextStyle(fontSize: 40.0, fontWeight: FontWeight.w400)), + Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Icon(Icons.air), + Text("${(weatherData.list!.first.wind!.speed! * 3.6).toStringAsFixed(1)}km/h", style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w400)), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + height: size.height * 0.2, + width: size.width * 0.5, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.0)), + color: Colors.grey, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: List.generate( + nbrNextHours, + (index) { + final weatherForecast = weatherData.list![index]; + return Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + height: size.height * 0.25, + width: size.width * 0.08, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.0)), + color: kBackgroundGrey, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center(child: CachedNetworkImage(imageUrl: "https://openweathermap.org/img/wn/${weatherForecast.weather!.first.icon!}.png")), + Text('${weatherForecast.main!.temp!.round().toString()} °C', style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.w600)), + Text('${formatTimestamp(weatherForecast.dt!, appContext, true, false)}', style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w400)), + ], + ), + ), + ); + }, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + height: size.height * 0.25, + width: size.width * 0.75, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.0)), + color: Colors.amber, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: List.generate( + nbrNextHours, + (index) { + final weatherForecastNextDay = getNextFiveDaysForecast(weatherData.list!)[index]; + return Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + height: size.height * 0.2, + width: size.width * 0.125, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.0)), + color: kBackgroundGrey, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center(child: CachedNetworkImage(imageUrl: "https://openweathermap.org/img/wn/${weatherForecastNextDay.weather!.first.icon!}@2x.png")), + Text('${weatherForecastNextDay.main!.temp!.round().toString()} °C', style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.w600)), + Text('${formatTimestamp(weatherForecastNextDay.dt!, appContext, false, true)}', style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w400)), + ], + ), + ), + ); + }, + ), + ), + ), + ) + ], + ), + ), + ], + ), + ), + ); + }, + ); + }, + child: Container( + //color: Colors.amber, + width: 70, + child: Column( + children: [ + CachedNetworkImage(imageUrl: "https://openweathermap.org/img/wn/${weatherData.list!.first.weather!.first.icon!}.png"), + Container(child: AutoSizeText("${weatherData.list!.first.main!.temp!.round().toString()} °C")), + //AutoSizeText(weatherData.city!.name!), + ], + ), + ), + ); + } +} + +//_webView \ No newline at end of file