본문 바로가기
관리자

Programming-[CrossPlatform]/Flutter

Flutter 기본-14. 날씨 앱 만들기 데이터, 이미지 연동, Key 개념 이해하기

728x90
반응형

Youtube 코딩셰프님의 강의를 요약 정리한 글이다. dart 언어나 이론 부분은 자바와 유사하여 대부분 제외하였고, flutter 기초 위주로 정리한다.

https://www.youtube.com/@codingchef

 

코딩셰프

향후 대세가 될 플러터를 단계별로 맛있게 학습하실 수 있습니다!

www.youtube.com

 


 

 

이제 앱을 완성한다. 앞서 배웠던 위젯 트리 구성이나 데이터 연동 부분 외에 직접 작업하면서 배웠던 점들을 기록한다. 강의에서 배운 부분들도 있지만, 대부분이 내가 마음대로 작성한거라 최적의 방식으로 코딩하지는 않았다. 나중에 best practice를 배우게 되면 더 좋을 것 같다.

 

 

1. 아이콘(그림 연동)

 

웹 이미지 연동 : Image.network(myUrl)

위 앱 화면의 그림상 clear sky라고 되어있는 부분의 왼쪽은 OpenWeather에서 제공하는 날씨 조건에 따른 icon이다. weather condition에 따른 표시 아이콘 목록 및 description 정보가 아래 페이지에 기재되어있다.

https://openweathermap.org/weather-conditions

 

class _WeatherScreenState extends State<WeatherScreen> {
  ///중략
  String? iconUrl;

  @override
  void initState() {
    var weatherData = widget.weatherData;
    
    cityName = weatherData['name'];
    temp = weatherData['main']['temp'].round();
    desc = weatherData['weather'][0]['description'];

    var iconCode = weatherData['weather'][0]['icon'];
    iconUrl = 'http://openweathermap.org/img/wn/${iconCode}@2x.png';

  }

 

앞서 Loading 위젯에서 API 통신을 통해 JSON 형식으로 받아온 weatherData에서 description과 iconCode를 얻어왔다. 그리고 여기서 얻은 iconCode를 icon 그림을 얻기 위한 iconUrl에 동적으로 입력되도록 만들어주었다. 그리고나서 이렇게 얻은 결과를

 

Image.network('url')

 

부분에 넣어주어 이미지 파일이 그대로 매칭될 수 있도록 했다. 기존 SvgPicture로 정적인 데이터를 넣은 부분은 주석처리하였다.

 

Row(
  children: [
    // SvgPicture.asset('svg/climacon-sun.svg'),
    Image.network(iconUrl!),
    Text(
      '${desc}',
      style: GoogleFonts.lato(
          fontSize: 12.0,
          fontWeight: FontWeight.bold,
          color: Colors.white),
    ),
  ],
)

 

 

정적 이미지 분기처리 연동

 

 

OpenWeatherAPI에서 제공하는 대기질지수 정보에 따라 얼굴 표정 모양의 아이콘을 동적으로 변경해주기 위해서 분기처리하여 이미지 디렉토리를 매칭하는 작업을 하였다. 강의에서는 if문을 통해 매칭하였는데, Map 자료구조도 써볼겸 Map 구조로 처리하였다.

 

 

const String imagePath = 'images/';
//class보다 위쪽에 상수 선언
class _WeatherScreenState extends State<WeatherScreen> {
//다른 변수들 생략
  late List<String> aqiData;
  late double particleAmount;
  late double superParticleAmount;

  @override
  void initState() {
    var weatherData = widget.weatherData;
    var airPollutionData = widget.airPollutionData;
    //다른 변수들 생략

    aqiData = getAqi(airPollutionData['list'][0]['main']['aqi']);

List<String> getAqi(int aqi) {
  HashMap<int, List<String>> aqiMap = HashMap.of({
    1: ["매우 좋음", '${imagePath}good.png'],
    2: ["좋음", '${imagePath}fair.png'],
    3: ["보통", '${imagePath}moderate.png'],
    4: ["나쁨", '${imagePath}poor.png'],
    5: ["매우 나쁨", '${imagePath}bad.png'],
  });
  if (aqiMap.containsKey(aqi)) {
    return aqiMap[aqi]!;
  } else {
    throw Exception("aqi is not in 1~5");
  }
}

 

initState 메서드에서 getAqi(int aqi)를 통해 image의 path를 동적으로 변경할 수 있도록 했다. 파라미터인 aqi 값은 widget.airPollutionData에서 얻어왔다. 이를 위해 기존 Network 클래스에 airPollution 데이터를 얻어오는 부분을 추가했다.

 

import 'dart:convert';
import 'package:http/http.dart' as http;

const String openWeatherApiKey = '.......';

class Network {

  final String openWeatherUrl;
  final String openWeatherAirPollutionUrl;

  Network(this.openWeatherUrl, this.openWeatherAirPollutionUrl);

  Future<dynamic> getParsedWeatherData() async {
    var response = await http.get(Uri.parse(openWeatherUrl));
    if(response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception("no weather Data");
    }
  }

  Future<dynamic> getParsedAirPollutionData() async {
    var response = await http.get(Uri.parse(openWeatherAirPollutionUrl));
    if(response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception("no air-pollution Data");
    }
  }
}

 

 

 

2. Navigator 적용

 

 

기존 로딩화면에서 사용자가 굳이 버튼을 누르지 않더라도 바로 날씨 정보 화면으로 이동할 수 있도록 Loading class의 initState에 Navigator를 적용한 getData() 함수가 실행되도록 했다.

 

class _LoadingState extends State<Loading> {
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    getData();
  }

 

 

그리고 날씨 정보 화면에서도 새로 고침할 수 있도록 appBar의 action 버튼의 onPressed에 Navigator.push를 적용했다. 새로 고침 버튼을 누르면 데이터가 다시 불러와지고 수정되는 방식은 뒤에서 배운다.

 

actions: [
  IconButton(
    icon: Icon(
      Icons.location_searching,
    ),
    onPressed: () {
      Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => const Loading()));
    },
    iconSize: 30.0,
  )
],

 

 

 

 

 

 

3. Key 개념 이해하기

 

Stateful 위젯을 만들면 기본 생성자에 key값이 자동으로 삽입된다. 이 key라는 것은 무엇일까?

class Loading extends StatefulWidget {
  const Loading({
    super.key,
  });

 

key는 위젯의 State를 보존하기를 원할 때 사용한다. key를 사용하면 어떤 위젯이나 요소들을 유니크하게 식별할 수 있게 된다.

https://api.flutter.dev/flutter/material/ReorderableListView-class.html

강의에서 나온 예시인데, 위 url에 들어가보면 TODO list 앱의 예제를 확인할 수 있다. 여기서 코드상, ListTile이라는 위젯을 써서 각 Item들을 표시하는데, 이 Item들은 똑같은 위젯이 반복되고 각 위젯들의 이름이 같으므로 구분자가 없다. 구분자가 있어야만 각 Item의 위치가 바뀌었을 때도 플러터가 각 앱의 위치를 정확히 확인할 수 있을 것이다. 이럴때 필요한 것이 key이다.

 

대표적인 key class의 종류는 다음과 같다.

  • UniqueKey(): 유일값으로 임의의 키값을 지정
  • ValueKey(1): 특정 값을 지정
  • Globalkey<T>() : 프로그램 전체에서 전역 변수처럼 키값을 지정

 

위와 같은 방식으로 key값을 할당해줄 수 있다.

728x90
반응형