오늘하루도 우힣ㅎ

Flutter) FutureBuilder를 이용한 API 사용 본문

flutter/Etc

Flutter) FutureBuilder를 이용한 API 사용

우힣 2020. 6. 23. 23:40

이전에 FutureBuilder에 관하여 포스팅을 한적이 있다 거기서는 네트워크를 통하여 데이터를 받아오지 않고 단지 delay를 주어 사용하는 방법을 썼었다. 그래서 이번에는 Open Weather에서 제공해주고 있는 날씨 API를 이용하여 FutureBuilder를 사용법을 알아볼려고 합니다!!

 

먼저 Open Weather에서 제공해주는 API를 사용하기 위해서는 key 발급이 필요합니다. 아래의 링크에서 가입을하게 되면 key바로 발급을 해줍니다. (연습용으로만 사용한다면 과금이 될 위험은 그리 크지 않아요, 근데 무료로 쓰면 날씨를 2시간마다 업데이트 한다는 말이...)

https://home.openweathermap.org

 

Members

Enter your email address and we will send you a link to reset your password.

home.openweathermap.org

1. API의 사용법 

https://openweathermap.org/current

 

Current weather data - OpenWeatherMap

Access current weather data for any location on Earth including over 200,000 cities! Current weather is frequently updated based on global models and data from more than 40,000 weather stations. Data is available in JSON, XML, or HTML format. We provide 40

openweathermap.org

여기는 API에 관한 공식 문서입니다. 가장 쉽게 사용할수 있는 방법은 https://api.openweathermap.org/data/2.5/weather?q={city name}&appid={your api key} 와 같은 형식을 사용 하는 것입니다. city name에는 자신이 원하는 지역, your api key에는 발급받은 api key만 넣어주면 됩니다!!

크롬에서 호출을 한번 해보면 아래와 같은 형식으로 보여지게 된다.(저는 마지막에 units=metric옵션을 추가해주어 섭씨 온도로 바꾸어 주었습니다. 해당 방식의 기본 호출은 화씨더군요...) 

해당 json 형식에서 제공해주는것을 보면 위도,경도, 흐림 정도 등 뭐 그런걸 제공 해주고 있습니다. 그리고 여러가지 도시의 정보를 한번에 불러오는 방법도 있으니 원하신다면 위의 공식 사이트를 보면 좋을것 같습니다.

 

2. Flutter

해당 프로젝트를 위해서 필요한것은 main.dart(날씨를 그려주기 위한 부분),weather_model.dart 이다. 그리고 네트워크를 통하여 데이터를 받아오기 위해서는 http라는 package또한 필요하다. 이것을 통하여 api를 호출하고 받아오게 됩니다.(이런걸 만드는 사람들은 대체...진짜 너무 감사합니다...)

https://pub.dev/packages/http

 

http | Dart Package

A composable, multi-platform, Future-based API for HTTP requests.

pub.dev

2-1. pubspec_yaml.dart에 먼저 필요한 것들을 추가해주어야 한다.

 - http: ^0.12.1 (api 호출을 위해 필요한 패키지)

2-2. 필요한 코드 목록

  • 날씨를 받아오게 되면 저장을 위한 모델 

  • 날씨를 받아오기 위해 사용되는 Future<Weather> 클래스

  • 받아온 정보를 보여주기 위한 UI부분

이렇게 3개만 쉽게 코딩해주면 됩니다.

 

먼저 날씨를 받아와 저장을 위한 모델은 다음과 같습니다.(이때 모델은 자신이 원하는 정보에 맞추어 받아 오면 됩니다. 저의 경우 현재 온도, 최저, 최고온도, 흐림의 정도, 흐림의 정도를 나타내는 코드를 받아 오고 있습니다.)

class Weather {
  final double temp;//현재 온도
  final int tempMin;//최저 온도
  final int tempMax;//최고 온도
  final String weatherMain;//흐림정도
  final int code;//흐림정도의 id(icon 작업시 필요)

  Weather({
    this.temp,
    this.tempMin,
    this.tempMax,
    this.weatherMain,
    this.code,
  });
}

 

다음으로, Future<Weather>의 경우도 저번 FutureBuilder에서 한것과 동일하게 생각하시면 쉽게 하실수 있습니다.

//필요 임포트 파일
import 'dart:convert';//json으로 바꿔주기 위해 필요한 패키지

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;//api 호출을 위해 필요한 패키지(as를 이용하여 해당 패키지의 이름을 설정해준다.)
import 'weather_model.dart';//날씨정보를 저장하기 위한 패키지




Future<Weather> getWeather() async {
	//api 호출을 위한 주소
    String apiAddr =
        "https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=여기는apikey자리에요&units=metric";
    http.Response response;//http request의 결과 즉 api 호출의 결과를 받기 위한 변수
    var data1;//api 호출을 통해 받은 정보를 json으로 바꾼 결과를 저장한다.
    Weather weather;
    
    try {
      response = await http.get(apiAddr);//필요 api 호출
      data1 = json.decode(response.body);//받은 정보를 json형태로 decode
      //받은 데이터정보를 필요한 형태로 저장한다.
      weather = Weather(
          temp: data1["main"]["temp"],
          tempMax: data1["main"]["temp_max"],
          tempMin: data1["main"]["temp_min"],
          weatherMain: data1["weather"][0]["main"],//weather부분의 경우 리스트로 json에 들어가고 있기 때문에 첫번째것을 쓴다고 표시를 해준다.
          code: data1["weather"][0]["id"]);//weather부분의 경우 리스트로 json에 들어가고 있기 때문에 첫번째것을 쓴다고 표시를 해준다.
    } catch (e) {
      weather = null//
      print(e);
    }

    return weather;
  }

마지막으로는 데이터를 보여주기 위한 UI부분을 구현할려 합니다. 저는 현재 온도, 최고 온도, 최저온도. 흐림정도를 아이콘으로 보여주는 기본적인 모습을 그릴려 합니다.

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: FutureBuilder(
              future: getWeather(),//future작업을 진행할 함수
              //snapshot은 getWeather()에서 return해주는 타입에 맞추어 사용한다.
              builder: (context, AsyncSnapshot<Weather> snapshot) {
                //데이터가 만약 들어오지 않았을때는 뱅글뱅글 로딩이 뜬다
                if (snapshot.hasData == false) {
                  return CircularProgressIndicator();
                }
                //데이터가 제대로 불러와진 경우 현재온도, 최저,최고 온도와 코드에 따른 아이콘을 표시하는 부분
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text('현재 온도 : ${snapshot.data.temp.toString()}'),
                    Text('최저 온도 : ${snapshot.data.tempMin.toString()}'),
                    Text('최고 온도 : ${snapshot.data.tempMax.toString()}'),
                    //아이콘의 경우 적절한것이 기본적으로 제공이 되지 않고 있다. 제대로된 앱을 위해서는 적절한 이미지를 삽입하는것이 옳은것 같다.
                    snapshot.data.code == 800
                        ? Icon(Icons.wb_sunny)
                        : snapshot.data.code / 100 == 8 ||
                                snapshot.data.code / 100 == 2
                            ? Icon(Icons.wb_cloudy)
                            : snapshot.data.code / 100 == 3 ||
                                    snapshot.data.code / 100 == 5
                                ? Icon(Icons.beach_access)
                                : snapshot.data.code / 100 == 6
                                    ? Icon(Icons.ac_unit)
                                    : Icon(Icons.cloud_circle)
                  ],
                );
              })),
    );
  }

======================전체 코드 와 결과======================

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'weather_model.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Weather Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Weather Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  Future<Weather> getWeather() async {
    String apiAddr =
        "https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=apikey를입력해주세요&units=metric";
    http.Response response;
    var data1;
    Weather weather;
    try {
      response = await http.get(apiAddr);
      data1 = json.decode(response.body);
      weather = Weather(
          temp: data1["main"]["temp"],
          tempMax: data1["main"]["temp_max"],
          tempMin: data1["main"]["temp_min"],
          weatherMain: data1["weather"][0]["main"],
          code: data1["weather"][0]["id"]);
      print(weather.tempMin);
    } catch (e) {
      print(e);
    }

    return weather;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: FutureBuilder(
              future: getWeather(),
              builder: (context, AsyncSnapshot<Weather> snapshot) {
                if (snapshot.hasData == false) {
                  return CircularProgressIndicator();
                }
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text('현재 온도 : ${snapshot.data.temp.toString()}'),
                    Text('최저 온도 : ${snapshot.data.tempMin.toString()}'),
                    Text('최고 온도 : ${snapshot.data.tempMax.toString()}'),
                    snapshot.data.code == 800
                        ? Icon(Icons.wb_sunny)
                        : snapshot.data.code / 100 == 8 ||
                                snapshot.data.code / 100 == 2
                            ? Icon(Icons.wb_cloudy)
                            : snapshot.data.code / 100 == 3 ||
                                    snapshot.data.code / 100 == 5
                                ? Icon(Icons.beach_access)
                                : snapshot.data.code / 100 == 6
                                    ? Icon(Icons.ac_unit)
                                    : Icon(Icons.cloud_circle)
                  ],
                );
//                    Text(snapshot.data.tempMax.toString());
              })),
    );
  }
}

 

 API를 사용 하는 것은 그렇게 어려운 과정이 아닙니다. 기회가 되신 다면 여러가지 방식으로 호출을 해보면서 자신만의 날씨 앱을 만들어 보면 좋을거 같습니다. 그리고 파파고 api도 정말 사용하기 쉽게 되있었던걸로 기억합니다. 연습을 해보고 싶으신 분은 파파고 api를 통해서 번역기를 만들어 보는것도 좋은 연습이 되지 않을까 생각을 합니다.(물론 개인적인 생각...)

Comments