공공 데이터인 날씨 api를 사용하여 달력에 기온 등을 표시하고
해당 날씨에 맞는 옷차림과 음식을 추천해주는 App이다.
default.mp4
비동기를 사용하기 위해 async & await 개념을 flutter에서 사용한다.
비동기를 사용하는 목적은 외부에서 data를 조작할 수 있도록 하기 위해서이다.
비동기화는 외부에서 데이터를 가져오는 동안 다른 작업을 수행하다
비동기화를 사용한 데이터가 준비완료 된다면 사용하는 방식이다.
- Flutter에서 비동기를 사용할 때 Future, async, await를 사용한다.
- 함수 이름 앞 Future은 비동기의 반환을 나타낸다. 가독성을 위해 적는것을 추천하지만, 생략해도 무방하다.
- await를 사용하기 위해서는 반드시 async가 적혀있어야한다.
- await를 사용하면 비동기 함수가 끝날때까지 기다리며, await를 사용하지않으면 기다리지않는다.
- 비동기함수가 끝났음을 알리고싶다면 Callback함수를 이용하여 알릴 수 있다.
- 사용 코드
Future<void> method async {
var temp = await something_method;
}
async-await에서 자세한 정보를 알 수 있다.
- 전달하기
loading.dart
import 'package:provider/provider.dart';
void getInfo () async{
WeatherInfo info = WeatherInfo();
await info.loadLocation();
await info.loadWeatherInfo();
await info.loadPastWeatherInfo(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day-2));
if(!mounted) return; // widget이 화면에 장착되면 mounted는 true
Navigator.push(context, // widget 화면 변경시 Navigator
MaterialPageRoute(builder:
(context) => MaterialApp(
home: Provider( // Provider로 데이터를 전달할 수 있다.
create: (context) => info, // info 라는 변수를 TodayWeather에게 전달한다
child: TodayWeather()))));
}
- 사용하기
weather.dart, food.dart, clothes.dart
// Provider.of<WeatherInfo>(context) 자체가 loading.dart의 info 변수가 되는 것이다.
Provider.of<WeatherInfo>(context).원하는 변수 또는 메소드
location.dart
import 'package:geolocator/geolocator.dart'; // 위치 정보 가져오기
import 'package:permission_handler/permission_handler.dart'; // 위치 권한 가져오기
class MyLocation {
late double myLatitude;
late double myLongitude;
Future<void> getMyCurrentLocation() async {
var statusLocation = await Permission.location.request(); // 권한 요청
if (statusLocation.isGranted) // 권한이 있다면
{
// 위치 정보 요청하기
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
myLatitude = position.latitude;
myLongitude = position.longitude;
}
else
{
print("위치 권한이 필요합니다.");
}
}
}
- API 정보 가져오기
network.dart
import 'dart:convert'; // Json 파일 다루기
import 'package:http/http.dart' as http; // http에서 api 정보 가져오기
class Network {
late String url;
Network(this.url); // 생성자
Future<dynamic> getJsonData() async{
http.Response response = await http.get(Uri.parse(url)); // url을 통해 Json 데이터 가져오기
if (response.statusCode == 200)
{
String jsonData = response.body;
var parsingData = jsonDecode(jsonData);
return parsingData;
}
else
{
print("error");
}
}
}
기상청 데이터를 요청하기 위해 필요한 데이터 변환
class ConvGridGps {
static const double RE = 6371.00877; // 지구 반경(km)
static const double GRID = 5.0; // 격자 간격(km)
static const double SLAT1 = 30.0; // 투영 위도1(degree)
static const double SLAT2 = 60.0; // 투영 위도2(degree)
static const double OLON = 126.0; // 기준점 경도(degree)
static const double OLAT = 38.0; // 기준점 위도(degree)
static const double XO = 43; // 기준점 X좌표(GRID)
static const double YO = 136; // 기1준점 Y좌표(GRID)
static const double DEGRAD = Math.pi / 180.0;
static const double RADDEG = 180.0 / Math.pi;
static double get re => RE / GRID;
static double get slat1 => SLAT1 * DEGRAD;
static double get slat2 => SLAT2 * DEGRAD;
static double get olon => OLON * DEGRAD;
static double get olat => OLAT * DEGRAD;
static double get snTmp =>
Math.tan(Math.pi * 0.25 + slat2 * 0.5) /
Math.tan(Math.pi * 0.25 + slat1 * 0.5);
static double get sn =>
Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(snTmp);
static double get sfTmp => Math.tan(Math.pi * 0.25 + slat1 * 0.5);
static double get sf => Math.pow(sfTmp, sn) * Math.cos(slat1) / sn;
static double get roTmp => Math.tan(Math.pi * 0.25 + olat * 0.5);
static double get ro => re * sf / Math.pow(roTmp, sn);
static gridToGPS(int v1, int v2) {
var rs = {};
double theta;
int xn = (v1 - XO).toInt();
int yn = (ro - v2 + YO).toInt();
var ra = Math.sqrt(xn * xn + yn * yn);
if (sn < 0.0) ra = -ra;
var alat = Math.pow((re * sf / ra), (1.0 / sn));
alat = 2.0 * Math.atan(alat) - Math.pi * 0.5;
if (xn.abs() <= 0.0) {
theta = 0.0;
} else {
if (yn.abs() <= 0.0) {
theta = Math.pi * 0.5;
if (xn < 0.0) theta = -theta;
} else
theta = Math.atan2(xn, yn);
}
var alon = theta / sn + olon;
rs['lat'] = alat * RADDEG;
rs['lng'] = alon * RADDEG;
return rs;
}
static gpsToGRID(double v1, double v2) {
var rs = {};
double theta;
var ra = Math.tan(Math.pi * 0.25 + (v1) * DEGRAD * 0.5);
ra = re * sf / Math.pow(ra, sn);
theta = v2 * DEGRAD - olon;
if (theta > Math.pi) theta -= 2.0 * Math.pi;
if (theta < -Math.pi) theta += 2.0 * Math.pi;
theta *= sn;
rs['x'] = (ra * Math.sin(theta) + XO + 0.5).floor();
rs['y'] = (ro - ra * Math.cos(theta) + YO + 0.5).floor();
return rs;
}
}
const Map<String, String> LocationCode = {
"서울" : "11B00000",
"인천" : "11B00000",
"경기도" : "11B00000",
"강원" : "11D10000",
"대전" : "11C20000",
"세종" : "11C20000",
"충청남도" : "11C20000",
"충청북도" : "11C10000",
"광주광역시" : "11F20000",
"전라남도" : "11F20000",
"전라북도" : "11F10000",
"대구" : "11H10000",
"경상북도" : "11H10000",
"부산" : "11H20000",
"울산" : "11H20000",
"경상남도" : "11H20000",
"제주" : "11G00000",
};
myAddress = await Network('https://dapi.kakao.com/v2/local/geo/coord2regioncode.json?x=$myLongitude&y=$myLatitude ').getRestAPIJsonData(restApiKey); // 나의 좌표를 입력하면 주소로 변환하는 카카오 API를 사용
LocationCode.forEach((key, value) { // Map 데이터인 LocationCode를 반복한다.
if (myAddress['documents'][0]['address_name'].contains(key)){ // 주소로 변환된 곳에 알맞는 key가 있다면
myAddressCode = value; // 그 코드를 변수에 담는다.
}
});
loading.dart
import 'package:flutter_spinkit/flutter_spinkit.dart';
flutter_spinkit을 활용하여 만들 수 있다.
- API 데이터를 flutter widget끼리 주고 받는 과정이 어려웠다.
- 날씨 API의 데이터의 형식이 일정하지 않았기 때문에 데이터 처리가 번거로웠다.