'Flutter OpenWeatherMap api fetch forecast
So... I got this One Call Api which also includes forecast for 7 days. I managed to get display the current weather (temp, and icon) but how do I get the forecast for the next 4 days?
My model:
class CurrentWeatherModel {
final WeatherInfo weatherInfo;
final double temp;
const CurrentWeatherModel({
required this.weatherInfo, required this.temp
});
factory CurrentWeatherModel.fromJson(json){
return CurrentWeatherModel(
weatherInfo: WeatherInfo.fromJson(json['weather'][0]),
temp: json['temp'],
);
}
}
class HourlyWeatherModel {
final double temp;
final WeatherInfo weatherInfo;
const HourlyWeatherModel(
{
required this.temp,
required this.weatherInfo
}
);
factory HourlyWeatherModel.fromJson(json){
return HourlyWeatherModel(
temp: json['temp'],
weatherInfo: WeatherInfo.fromJson(json['weather'][0])
);
}
}
class DailyWeatherInfoModel {
final TempModel dailyTemp;
final WeatherInfo weatherInfo;
final DateTime date;
final int dt;
const DailyWeatherInfoModel(
{
required this.dailyTemp,
required this.weatherInfo,
required this.date,
required this.dt,
}
);
factory DailyWeatherInfoModel.fromJson(json){
return DailyWeatherInfoModel(
dailyTemp: TempModel.fromJson(json['temp']),
dt: json['dt'],
date: DateTime.fromMillisecondsSinceEpoch(json['dt'] * 1000,
isUtc: true),
weatherInfo: WeatherInfo.fromJson(json['weather'][0]),
);
}
}
class TempModel {
final double day;
final double min;
final double max;
const TempModel(
{
required this.day,
required this.min,
required this.max
}
);
factory TempModel.fromJson(json){
return TempModel(
day: json['day'],
min: json['min'],
max: json['max']
);
}
}
class WeatherInfo {
final String? description;
final String? icon;
WeatherInfo({this.description, this.icon});
factory WeatherInfo.fromJson(Map<String, dynamic> json) {
final description = json['description'];
final icon = json['icon'];
return WeatherInfo(description: description, icon: icon);
}
}
class WeatherForecastResponse {
final CurrentWeatherModel current;
final HourlyWeatherModel hourly;
final DailyWeatherInfoModel daily;
String get iconCurrentWeatherUrl{
return 'https://merakiapp.be/wp-content/uploads/2022/04/${current.weatherInfo.icon}.png';
}
String get iconDailyWeatherUrl{
return 'https://merakiapp.be/wp-content/uploads/2022/04/${daily.weatherInfo.icon}.png';
}
const WeatherForecastResponse(
{
required this.current,
required this.daily,
required this.hourly
}
);
factory WeatherForecastResponse.fromJson(json){
return WeatherForecastResponse(
current: CurrentWeatherModel.fromJson(json['current']),
hourly: HourlyWeatherModel.fromJson(json['hourly'][0]),
daily: DailyWeatherInfoModel.fromJson(json['daily'][0])
);
}
}
My Response:
Future<WeatherForecastResponse> getForecast(double lat, double lon) async {
try {
String api = 'https://api.openweathermap.org/data/2.5/onecall';
String appId = 'MyAPIKey';
String units = 'metric';
String cnt = '4';
String url = '$api?lat=$lat&lon=$lon&cnt=$cnt&appid=$appId&units=$units';
final response = await http.get(Uri.parse(url));
final json = jsonDecode(response.body);
return WeatherForecastResponse.fromJson(json);
} catch (e) {
rethrow;
}
}
Respons object:
void _currentWeather() async {
await FirebaseFirestore.instance.collection('Users').doc(currentUser).get().then((value) => _userLocation = value.data()!['Location']);
setState(() {
lat = _userLocation.latitude;
lon = _userLocation.longitude;
});
final response = await _dataWeatherService.getForecast(lat!, lon!);
setState(() {
temp = response.current.temp.round();
weather = response.current.weatherInfo.description;
icon = response.iconCurrentWeatherUrl;
});
}
void _dailyForecast() async {
final response = await _dataWeatherService.getForecast(lat!, lon!);
setState(() {
tempDaily = response.daily.dailyTemp.day.round();
weatherDaily = response.daily.weatherInfo.description;
iconDaily = response.iconDailyWeatherUrl;
date = response.daily.date;
});
}
JSON from OneCall API:
{
"lat": 33.44,
"lon": -94.04,
"timezone": "America/Chicago",
"timezone_offset": -21600,
"current": {
"dt": 1618317040,
"sunrise": 1618282134,
"sunset": 1618333901,
"temp": 284.07,
"feels_like": 282.84,
"pressure": 1019,
"humidity": 62,
"dew_point": 277.08,
"uvi": 0.89,
"clouds": 0,
"visibility": 10000,
"wind_speed": 6,
"wind_deg": 300,
"weather": [
{
"id": 500,
"main": "Rain",
"description": "light rain",
"icon": "10d"
}
],
"rain": {
"1h": 0.21
}
},
"minutely": [
{
"dt": 1618317060,
"precipitation": 0.205
},
...
},
"hourly": [
{
"dt": 1618315200,
"temp": 282.58,
"feels_like": 280.4,
"pressure": 1019,
"humidity": 68,
"dew_point": 276.98,
"uvi": 1.4,
"clouds": 19,
"visibility": 306,
"wind_speed": 4.12,
"wind_deg": 296,
"wind_gust": 7.33,
"weather": [
{
"id": 801,
"main": "Clouds",
"description": "few clouds",
"icon": "02d"
}
],
"pop": 0
},
...
}
"daily": [
{
"dt": 1618308000,
"sunrise": 1618282134,
"sunset": 1618333901,
"moonrise": 1618284960,
"moonset": 1618339740,
"moon_phase": 0.04,
"temp": {
"day": 279.79,
"min": 275.09,
"max": 284.07,
"night": 275.09,
"eve": 279.21,
"morn": 278.49
},
"feels_like": {
"day": 277.59,
"night": 276.27,
"eve": 276.49,
"morn": 276.27
},
"pressure": 1020,
"humidity": 81,
"dew_point": 276.77,
"wind_speed": 3.06,
"wind_deg": 294,
"weather": [
{
"id": 500,
"main": "Rain",
"description": "light rain",
"icon": "10d"
}
],
"clouds": 56,
"pop": 0.2,
"rain": 0.62,
"uvi": 1.93
},
...
},
"alerts": [
{
"sender_name": "NWS Tulsa",
"event": "Heat Advisory",
"start": 1597341600,
"end": 1597366800,
"description": "...HEAT ADVISORY REMAINS IN EFFECT FROM 1 PM THIS AFTERNOON TO\n8 PM CDT THIS EVENING...\n* WHAT...Heat index values of 105 to 109 degrees expected.\n* WHERE...Creek, Okfuskee, Okmulgee, McIntosh, Pittsburg,\nLatimer, Pushmataha, and Choctaw Counties.\n* WHEN...From 1 PM to 8 PM CDT Thursday.\n* IMPACTS...The combination of hot temperatures and high\nhumidity will combine to create a dangerous situation in which\nheat illnesses are possible.",
"tags": [
"Extreme temperature value"
]
},
...
]
Solution 1:[1]
I would change your WeatherForecastResponse
to have a list of DailyWeatherInfoModel
vs a single model.
class WeatherForecastResponse {
final CurrentWeatherModel current;
final HourlyWeatherModel hourly;
final List<DailyWeatherInfoModel> daily;
...
}
Then you parse a full list in the fromJson
constructor
factory WeatherForecastResponse.fromJson(json) {
final dailyResponse = json['daily'] as List;
final dailyForecastList = <DailyWeatherInfoModel>[];
for (final day in dailyResponse) {
dailyForecastList.add(DailyWeatherInfoModel.fromJson(day));
}
return WeatherForecastResponse(
current: CurrentWeatherModel.fromJson(json['current']),
hourly: HourlyWeatherModel.fromJson(json['hourly'][0]),
daily: dailyForecastList);
}
I would also just put the icon url in each daily/hourly model. This way you have a fully self contained list of DailyWeatherInfoModel
with everything you need.
class DailyWeatherInfoModel {
final TempModel dailyTemp;
final WeatherInfo weatherInfo;
final DateTime date;
final int dt;
final String iconUrl; // adding this
const DailyWeatherInfoModel({
required this.dailyTemp,
required this.weatherInfo,
required this.date,
required this.dt,
required this.iconUrl,
});
factory DailyWeatherInfoModel.fromJson(json) {
final weatherInfo = WeatherInfo.fromJson(json['weather'][0]);
final iconUrl =
'https://merakiapp.be/wp-content/uploads/2022/04/${weatherInfo.icon}.png'; // init iconUrl here
return DailyWeatherInfoModel(
dailyTemp: TempModel.fromJson(json['temp']),
dt: json['dt'],
date: DateTime.fromMillisecondsSinceEpoch(json['dt'] * 1000, isUtc: true),
weatherInfo: weatherInfo,
iconUrl: iconUrl,
);
}
}
You can apply the same concept to the hourly forecast as well.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Loren.A |