공부 기록/Flutter

Flutter: 날씨 앱(weather app) 만들기 2: JSON parsing

naraewool 2022. 1. 6. 23:43
728x90

https://www.youtube.com/watch?v=ccq1yCmNzdk&list=PLQt_pzi-LLfoOpp3b-pnnLXgYpiFEftLB&index=14 

#1에서는 버튼을 누르면 날씨 정보를 가져왔는데, 앱이 실행되자마자 날씨 정보가 나타나도록 코드를 바꿔보자. 
onPressed를 null로 바꾸고, initState() 사용 

class _LoadingState extends State<Loading> {

  // _LoadingState 위젯이 생성될 때 딱 한번 호출되는 메서드 추가
  @override
  void initState() {
    super.initState();
        getLocation(); //initState 안에 추가함으로써 앱이실행되지마자 위치 정보가 뜰 수 있도록 수정함
  }

  void getLocation() async {
    // 현재 위치 가져오기
    Position position = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high);
    print(position);
  }

인터넷이 안되거나 위치정보 동의를 거부하는 경우와 같이 예외적인 상황을 위해 exeption handling 필요 -> try-catch문 사용. 예외의 경우는 일반적으로 'e'를 사용하는데, 다른 걸로 사용해도 괜찮음.

에러가 발생하지 않고 메시지가 출력되도록.

 

https://pub.dev/packages/http/install을 참고해 http package 설치.
공식 문서 참고하여 인터넷 허용까지 추가.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.weather">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> // 이전에 추가해둔 위치 동의
    <uses-permission android:name="android.permission.INTERNET" /> //인터넷 허용 동의

 

fetchData() 추가. get에 마우스를 올려보면 어떤 형태로 써야하는지 나옴.

문서를 보면, get()은 지정한 헤더들을 지정한 URL로 GET 요청을 보내는 함수이다. URL은 URI보다 더 작은 개념. 

 

API Reference > HTTP > Response를 살펴보면, Response란 미리 알려진 전체 응답 바디에 있는 HTTP 응답 클래스라고 한다.  여기서 새로운 인스턴스를 생성하고, 다양한 Properties를 이용하여 데이터를 사용할 수 있다. 

예를 들어, body 속성을 이용하면, 응답 바디 전체가

statusCode를 이용하면 상태코드가 나타난다. 

 

package 에 as http를 추가하여, get이 http 패키지로부터 왔다는 것을 명시 + get 함수를 불러올 때도 http. 표시해야함

이렇게 표기해줌으로써, 다른개발자가 은 http.Response를 보고 http 패키지로부터 나온 것을 파악한 후, 문서(> API reference)에서 이의 Response에 대한 정보를 확인할 수 있음

데이터 형식에 대해 알아보자면, XML은 html 태그와 비슷한데 (<p> <br>...), 다른 점은 사용자 임의로 태그를 만들고 키와 값을 설정하여 메타 정보를 추가할 수 있다. (오른쪽 그림 예시 참고)

JSON parsing을 해보자.  요청 성공시 (상태 코드 200) reponse body를 가져오고, 실패 시 에러메시지를 띄우도록.

- import 'dart:convert'; 문 추가 -> jsonDecode() 메서드를 사용할 수 있게됨. 인자값으로 응답 바디를 선언한 jsondata 변수를 전달해주자.
- myJson이라는 변수에 jsonDecode() 함수 호출  weather의 0번 인덱스에 있는 데이터 중 description을 가져와보자. 
  ㄴJSON의 값은 string, int 등 어떤 타입이 올지 모르므로 Dynamic 타입으로 지정해주어야 하기 때문에 변수 키워드를 'var'로 지정해주어야 한다.
  ㄴ 이 때 특이한 것은, jsonDecode(변수)['weather'][0]['description']; 형식으로 작성.

Troubleshooting: 여기서 나는  await get() 부분에서 에러가 발생하였다. 
-> http 버전을 강의에서 사용한 버전(http: ^0.12.2)으로 바꾸었더니, "Error: Cannot run with sound null safety, because the following dependencies don't support null safety: - package:http - package:http_parser"가 발생하였다.  null safety가 적용된 버전이라 그런 것 같다. . 이 블로그를 참고해서 안드로이드 스튜디오에서 --no-sound-null-safety 를 설정하니, 빌드가 잘 되었다!

의문) 왜 instance of 'Response'가 보이지?   Future<void> getLocation() async 에 Future를 붙여봐도 동일하네 +_+

// import 'dart:html';
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart' as http; // http
import 'dart:convert';

class Loading extends StatefulWidget {
  const Loading({Key? key}) : super(key: key);

  @override
  _LoadingState createState() => _LoadingState();
}

class _LoadingState extends State<Loading> {

  // _LoadingState 위젯이 생성될 때 딱 한번 호출되는 메서드 추가
  @override
  void initState() {
    super.initState();
    getLocation(); //initState 안에 추가함으로써 앱이실행되지마자 위치 정보가 뜰 수 있도록 수정함
    fetchData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: null,
          child: Text('Get my location'),
        ),
      ),
    );
  }

  void fetchData() async {

    http.Response response = await http.get('https://samples.openweathermap.org/data/2.5/weather?q=London&appid=b1b15e88fa797225412429c1c50c122a1');
    print(response);

    if(response.statusCode == 200){
      String jsonDada = response.body; // 성공하면 응답 바디를 가져옴
      var myJson = jsonDecode(jsonDada)['weather'][0]['description']; // weather[0]의 description만 가져옴
      var windSpeed = jsonDecode(jsonDada)['wind']['speed'];
      print(jsonDada);
      print(myJson);
      print(windSpeed);
    } else (response.statusCode); // 성공하지 않으면 상태코드를 보여줌 
  }

  Future<void> getLocation() async {
    try {
      Position position = await Geolocator.getCurrentPosition(
          desiredAccuracy: LocationAccuracy.high);
      print(position);
    } catch (e) {
      print('Internet connect problem');
    }
    //
    // void requestPermission() async {
    //   //권한 요청하기
    //   LocationPermission permission = await Geolocator.requestPermission();
    // }
  }
}