본문 바로가기
관리자

Programming-[CrossPlatform]/Flutter

Flutter 기본-12. 날씨 앱 만들기 기본2

728x90
반응형

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

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

 

코딩셰프

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

www.youtube.com

 


지난 글에 이어서 날씨 앱 만들기를 해본다. 중요한 개념들을 코드를 보며 살펴볼 것이다.

 

1. 객체지향적으로 짜기

 

아래와 같은 원칙을 갖고 코드를 짠다.

 

  • 파일 이름은 소문자로만 구성한다. snake_case로 작성한다.
  • 각 파일 및 클래스가 어떤 객체(명사)를 대표한다고 생각하고 이름을 짓는다. 예를 들어 my_location은 location을 얻어오는 역할을(역할만) 하고, 그 내부에 latitude, longitude라는 멤버 변수(속성)가 있다.
  • my_location, network간 의존성이 없도록한다. 이 두 객체를 loading.dart에서 따로 불러와서 조합하는 방식으로 구성하면 각 객체의 독립성이 유지된다.

my_location.dart

geolocator의 예제 코드를 복사해와서 예외처리를 하는 부분은 생략했다. 살펴볼 부분은 다음과 같다.

  • MyLocation이라는 클래스를 만들어 위치를 얻어올 수 있게 객체 지향적으로 바라봤고, latitude, longitude라는 속성값을 비동기로 넣어준다.
  • double?로 ?를 사용하여 async/await 비동기로 받아오는 부분을 nullable 처리했다.
  • try-catch 구문으로 getCurrentPosition이 실패했을 때 에러를 출력하도록 했다.
class MyLocation {

  double? latitude;
  double? longitude;

  Future<void> getPosition() async {
    bool serviceEnabled;
    LocationPermission permission;
    
    ... 중략
    
     try {
      Position position = await Geolocator.getCurrentPosition();
      latitude = position.latitude;
      longitude = position.longitude;
    } catch (e) {
      print(e);
    }
  }

 

network.dart

외부 api와 연결하는 역할을 하는 객체를 만들기 위해 Network 클래스를 생성했다.

API 키값은 고정된 값이므로 const로 정의하고 class 바깥쪽에서 할당했다. url은 비동기작업 후 한번만 할당되면 끝이므로 final로 정의했다.

 

const String OpenWeatherApiKey = 'ad5123ac4......';

class Network {

  final String openWeatherUrl;

  Network(this.openWeatherUrl);

  Future<dynamic> getParsedWeatherData() async {
    var response = await http.get(Uri.parse(openWeatherUrl));
    if(response.statusCode == 200) {
      return jsonDecode(response.body);
    }
  }
}

 

 

 

 

2. flutter 데이터 전달 구조 이해하기

 

loading.dart

  • State 클래스(_LoadingState) 내부에 메서드를 작성한다. 그래야 Navigator.push에서(또는 다른 메서드에서도) context를 불러올 수 있다.
  • MyLocation class와 Network class를 독립적으로 불러와서 서로 의존하지 않도록 해준다.
  • jsonDecode로 입력한 부분은 dynamic 타입으로 받았다.
    • var, dynamic: 모두 정해진 자료형이 아니라 입력된 값으로 자료형을 추론한다. var는 한 번 어떤 타입의 데이터가 입력되면 다른 타입으로 재할당이 불가하고, dynamic은 재할당이 가능하다.
  • WeatherScreen 클래스를 만들 때 속성값으로 weatherData를 넘겨주는 방식으로 만들었다. 이렇게 객체지향에서 객체 간 서로 데이터를 주고 받는 방식으로 코딩을 한다.
class Loading extends StatefulWidget {
  const Loading({
    super.key,
  });

  @override
  State<Loading> createState() => _LoadingState();
}

class _LoadingState extends State<Loading> {

... 중략

    void getData() async {
      try {
        MyLocation myLocation = MyLocation();
        myLocation.getPosition();
        double? latitude = myLocation.latitude;
        double? longitude = myLocation.longitude;
        final String url =
            'https://api.openweathermap.org/data/2.5/weather?lat=$latitude&lon=$longitude&appid=$OpenWeatherApiKey&units=metric';

        Network network = Network(url);
        dynamic parsedData = await network.getParsedWeatherData();

        Navigator.push(context,
            MaterialPageRoute(builder: (BuildContext context) {
          return WeatherScreen(
            weatherData: parsedData,
          );
        }));
      } catch (e) {
        print(e);
      }
    }
    
    ...
    
    @override
  Widget build(BuildContext context) {
  
  ...
  
  
   ElevatedButton(
                onPressed: () {
                  getData();
                },
                child: Text('Get My Location'),
                style: ElevatedButton.styleFrom(backgroundColor: Colors.grey),
              )
    
}

 

 

weather_screen.dart

 

loading.dart에서 받아온 데이터(weatherData: parsedData)를 dynamic 멤버 변수로 받아왔다. 생성자를 ({ }) 형태로 사용하여 named optional parameter로 두었다. WeatherScreen은 위젯이기 때문에 위젯을 불러올 때 key, weatherData라는 이름을 지정하여 불러올 수 있도록 이렇게 지정한다.

 

-initState가 실행된 후 build 메서드가 실행된다!

class WeatherScreen extends StatefulWidget {
  dynamic weatherData;

  WeatherScreen({Key? key, this.weatherData}) : super(key: key);

  @override
  State<WeatherScreen> createState() => _WeatherScreenState();
}

class _WeatherScreenState extends State<WeatherScreen> {

  double? latitude;
  double? longitude;
  String? cityName;

  @override
  void initState() {
    // super.initState();
    var weatherData = widget.weatherData;
    print(widget.weatherData);
    latitude = weatherData['coord']['lat'];
    longitude = weatherData['coord']['lon'];
    cityName = weatherData['name'];
  }



  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Row(
        children: [
          Column(
            children: [
              Text('$latitude')
            ],
          )
        ],
      ),
    );
  }
}

728x90
반응형