날씨와 미세먼지 농도 api를 가져와서 구현을 해보았다.
맨처음에 구글링 통해서 유튜브 강좌보고 api불러왔는데 똑같은 ui로 했을때는 잘 됐는데 막상 내 프로젝트해서 할때는 잘 안불러와졌다ㅜㅜ
그래서 많이 해맷는데 openweatherAPI 문서를 잘 읽고 다시 차근차근 하니까 이 방법으로 하니까 아주 잘 불러와졌다!!
날씨 데이터를 어디서 가져올지 찾아보던중 예전에 웹을 배울때 날씨 데이터를 가져온적이 있는데 그때 사용했던 openweathermap사이트가 생각이 나서 데이터는 openweathermap를 사용해서 가져왔다.
https://openweathermap.org/price
날씨와 미세먼지 두개 다 필요했는데 여기에 다 있었다.
다 무료는 아니고 몇몇개만 무료지만 내가 사용하고싶은 데이터는 다행히 무료였다.
일단 나는 현재 내가 위치하고 있는 지역의 날씨 데이터를 가져오고 싶다.
그래서 먼저 현재위치를 가져오는 데이터를 불러와 준다.
// 현재 위치 데이터 가져오기
Future<Position> fetchLocationName() async {
bool serivceEnabled = true;
LocationPermission permission;
serivceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serivceEnabled) {
return Future.error('Location services are disabled');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
return Future.error('error');
}
return await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
}
이렇게 현재 내 위치가 콘솔창에 잘 뜨면 성공!
다음으로 현재위치의 날씨 데이터를 불러와 볼 것 이다.
날씨 데이터를 불러올때는 api와 그 api에서 불러오고자하는 것을 모델링을 시켜줘야 한다.
내가 필요한 데이터는
강수량은 값이 없을때는 데이터가 나오지 않아서 null값을 줬다.
Model
class WeatherModel {
final double temp;
final String weatherMain; //흐림정도
final int humidity;
final double? rain;
final int code; //흐림정도의 id
WeatherModel({
required this.rain,
required this.humidity,
required this.temp,
required this.weatherMain,
required this.code,
});
Map<String, dynamic> toMap() {
return <String, dynamic>{
'temp': temp,
'weatherMain': weatherMain,
'humidity': humidity,
'rain': rain,
'code': code,
};
}
factory WeatherModel.fromMap(Map<String, dynamic> map) {
return WeatherModel(
temp: map['temp'] as double,
weatherMain: map['weatherMain'] as String,
humidity: map['humidity'] as int,
rain: map['rain'] != null ? map['rain'] as double : null,
code: map['code'] as int,
);
}
String toJson() => json.encode(toMap());
factory WeatherModel.fromJson(String source) => WeatherModel.fromMap(json.decode(source) as Map<String, dynamic>);
}
Controller
Future<WeatherModel?> getWeather() async {
Position position = await fetchLocationName();
//현재 위치 날씨 가져오기
String WeatherData =
'https://api.openweathermap.org/data/2.5/weather?lat=${position.latitude}&lon=${position.longitude}&limit&appid=${your_api_key}&units=metric';
try {
final response = await http.get(Uri.parse(WeatherData));
if (response.statusCode == 200) {
var data = json.decode(response.body);
print("데이터 확인: $data");
double? rain =
data["rain"] != null ? data["rain"]["1h"]?.toDouble() : null;
WeatherModel weather = WeatherModel(
temp: data["main"]["temp"], //온도
weatherMain: data["weather"][0]["main"], //날씨 아이콘
humidity: data["main"]["humidity"], // 습도
rain: rain, // 강수량
code: data["weather"][0]["id"], //날씨 id값
);
return weather;
} else {
throw Exception('Failed to load weather data');
}
} catch (e) {
print(e);
return null;
}
}
두가지 데이터를 불러올 것이다.
Model
class AirModel {
final double pm2_5;
final double pm10;
AirModel({required this.pm2_5, required this.pm10});
Map<String, dynamic> toMap() {
return <String, dynamic>{
'pm2_5': pm2_5,
'pm10': pm10,
};
}
factory AirModel.fromMap(Map<String, dynamic> map) {
return AirModel(
pm2_5: map['pm2_5'] as double,
pm10: map['pm10'] as double,
);
}
String toJson() => json.encode(toMap());
factory AirModel.fromJson(String source) =>
AirModel.fromMap(json.decode(source) as Map<String, dynamic>);
}
Controller
Future<AirModel?> getAir() async {
Position position = await fetchLocationName();
// 현재 위치 미세먼지 데이터 가져오기
String airData =
"http://api.openweathermap.org/data/2.5/air_pollution?lat=${position.latitude}&lon=${position.longitude}&appid=${your_api_key}";
try {
final response = await http.get(Uri.parse(airData));
if (response.statusCode == 200) {
var data = json.decode(response.body);
print("미세먼지확인: $data");
AirModel airdata = AirModel(
pm2_5: data['list'][0]['components']['pm2_5'],
pm10: data['list'][0]['components']['pm10'],
);
return airdata;
} else {
throw Exception('Failed to load air pollution data');
}
} catch (e) {
print(e);
return null;
}
}
Text('${airdata.pm2_5.round().toDouble()}',
초미세먼지 농도는 double로 불러줘도 값이 소숫점이 붙으면 자꾸 에러가 뜨길래 .toDouble()을 한번 더 써줬더니 해결이 됐다.
이 값으로 통해 사용자가 날씨를 쉽게 볼 수 있도록 날씨에 따라 변하는 이미지와 텍스트로 알려줄 수 있다.
Widget buildWeatherIcon(int? code) {
if (code == null) {
return Icon(Icons.error);
}
if (code == 800) {
return Image.asset("assets/Main/sun.png");
} else if (code == 801) {
return Image.asset("assets/Main/cloudysun.png");
} else if (code >= 802 && code <= 804) {
return Image.asset('assets/Main/cloud.png');
} else if (code >= 700 && code < 800) {
return Image.asset('assets/Main/cloud.png');
} else if (code >= 600 && code < 700) {
return Image.asset('assets/Main/snow.png');
} else if (code >= 500 && code < 600) {
return Image.asset('assets/Main/rain.png');
} else if (code >= 300 && code < 400) {
return Image.asset('assets/Main/rain.png');
} else if (code >= 200 && code < 300) {
return Image.asset('assets/Main/rain.png');
} else {
return Icon(Icons.error);
}
}
String getWatherStatus(String weatherMain) {
switch (weatherMain) {
case 'Clear':
return '맑음';
case 'Few clouds':
return '흐림';
case 'Clouds':
return '구름';
case 'Rain':
return '비';
case 'Mist' || 'Smoke' || 'Dust' || 'Fog' || 'Sand' || 'Haze':
return '안개';
case 'Drizzle':
return '이슬비';
case 'Thunderstorm':
return '뇌우';
case 'Snow':
return '눈';
default:
return weatherMain;
}
}
https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
수치와 아이디 값에 따른 아이콘과 기우 텍스트는 여기서 참고해서 작성하면 된다.