반응형
Udemy 플러터 강의 5주차
ㄴ Section 13. Clima - Powering Your Flutter App with Live Web Data
https://www.udemy.com/course/flutter-bootcamp-with-dart/
이번 강의에서는 geolocator package를 이용해서 위도, 경도에 대한 위치 정보를 가져온 뒤, API를 이용하여 해당 위치(또는 도시)의 날씨 정보를 가져오는 앱을 구축하는 과정에 대해 설명한다.
강의내용요약
Flutter
- geolocator Package
- Stateful Widget Lifecycle
- http Package
- Passing Data Backwards Through the Navigation Stack
Dart
- Futures, Async, Await
- Exception Handling, Null Aware Operations
- dynamic type
소스코드
location.dart
- geolocator package 사용
- 7.x 버전부터는 sdk 31로 교체 필요함
- async, await 사용
- Future 사용
- LocationAccuracy.low 옵션을 통해 정확도 설정 가능
import 'package:geolocator/geolocator.dart';
class Location {
late double latitude;
late double longitude;
Future<void> getCurrentLocation() async {
try {
// Geolocator API로 위도, 경도 호출
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.low,
forceAndroidLocationManager: true);
latitude = position.latitude;
longitude = position.longitude;
} catch (e) {
print(e);
}
}
}
networking.dart
- http Package 사용 (날씨 정보를 가져오는 API를 호출하기 위함)
- http Pacakge는 클래스 형태로 구현되어 있지 않기 때문에 가독성 향상을 위해 as http 명시
- dart:convert로 json 파싱
import 'package:http/http.dart' as http;
import 'dart:convert';
class NetworkHelper {
NetworkHelper(this.url);
final String url;
Future<void> getData() async {
// get method에 string인 url을 Uri.parse를 통해 변환 후 전달
http.Response response = await http.get(Uri.parse(url));
// response 정상인 경우 200 반환
if (response.statusCode == 200) {
String data = response.body;
return jsonDecode(data);
} else {
print(response.statusCode);
}
}
}
weather.dart
- 날씨 정보 API 호출 https://openweathermap.org/
- 도시 호출하는 API와 위도, 경도 호출하는 API 존재
import 'package:clima_flutter_flearner/services/location.dart';
import 'package:clima_flutter_flearner/services/networking.dart';
// apiKey 교체
const apiKey = '';
const openWeatherMapURL = 'https://api.openweathermap.org/data/2.5/weather';
class WeatherModel {
// 도시 호출하는 API 이용
Future<dynamic> getCityWeather(String cityName) async {
NetworkHelper networkHelper = NetworkHelper(
'$openWeatherMapURL?q=$cityName&appid=$apiKey&units=metric');
var weatherData = await networkHelper.getData();
return weatherData;
}
// 위도, 경도 호출하는 API 이용
Future<dynamic> getLocationWeather() async {
Location location = Location();
await location.getCurrentLocation();
NetworkHelper networkHelper = NetworkHelper(
'$openWeatherMapURL?lat=${location.latitude}&lon=${location.longitude}&appid=$apiKey&units=metric');
var weatherData = await networkHelper.getData();
return weatherData;
}
String getWeatherIcon(int condition) {
if (condition < 300) {
return '🌩';
} else if (condition < 400) {
return '🌧';
} else if (condition < 600) {
return '☔️';
} else if (condition < 700) {
return '☃️';
} else if (condition < 800) {
return '🌫';
} else if (condition == 800) {
return '☀️';
} else if (condition <= 804) {
return '☁️';
} else {
return '🤷';
}
}
String getMessage(int temp) {
if (temp > 25) {
return 'It\'s 🍦 time';
} else if (temp > 20) {
return 'Time for shorts and 👕';
} else if (temp < 10) {
return 'You\'ll need 🧣 and 🧤';
} else {
return 'Bring a 🧥 just in case';
}
}
}
loading_screen.dart
- Widget Lifecycle: initState -> build -> deactivate
- spinKitDoubleBounce로 로딩화면 표시
import 'package:clima_flutter_flearner/screens/location_screen.dart';
import 'package:flutter/material.dart';
import 'location_screen.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:clima_flutter_flearner/services/weather.dart';
class LoadingScreen extends StatefulWidget {
@override
_LoadingScreenState createState() => _LoadingScreenState();
}
class _LoadingScreenState extends State<LoadingScreen> {
@override
void initState() {
super.initState();
getLocationData();
}
void getLocationData() async {
// 클래스 초기화와 메소드 호출 동시에 가능
var weatherData = await WeatherModel().getLocationWeather();
Navigator.push(context, MaterialPageRoute(builder: (context) {
return LocationScreen(locationWeather: weatherData);
}));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SpinKitDoubleBounce(
color: Colors.white,
size: 100.0,
),
),
);
}
}
location_screen.dart
- widget을 통해 LocationScreen 변수 접근 가능
- city_screen에서 pop한 값을 typedName에서 받아옴
import 'package:clima_flutter_flearner/screens/city_screen.dart';
import 'package:flutter/material.dart';
import 'package:clima_flutter_flearner/utilities/constants.dart';
import 'package:clima_flutter_flearner/services/weather.dart';
import 'city_screen.dart';
class LocationScreen extends StatefulWidget {
LocationScreen({this.locationWeather});
final locationWeather;
@override
_LocationScreenState createState() => _LocationScreenState();
}
class _LocationScreenState extends State<LocationScreen> {
WeatherModel weather = WeatherModel();
late int temperature;
late String weatherIcon;
late String cityName;
late String weatherMessage;
@override
void initState() {
super.initState();
// LocationScreen의 값을 widget을 통해 접근 가능
updateUI(widget.locationWeather);
}
void updateUI(dynamic weatherData) {
setState(() {
if (weatherData == null) {
temperature = 0;
weatherIcon = 'Error';
weatherMessage = 'Unable to get weather data';
cityName = '';
return;
}
temperature = weatherData['main']['temp'].toInt();
var condition = weatherData['weather'][0]['id'];
weatherIcon = weather.getWeatherIcon(condition);
weatherMessage = weather.getMessage(temperature);
cityName = weatherData['name'];
print(cityName);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: const AssetImage('images/location_background.jpg'),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.white.withOpacity(0.8), BlendMode.dstATop),
),
),
constraints: const BoxConstraints.expand(),
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
TextButton(
// 현재 위치에 대한 날씨 정보 가져오는 버튼
onPressed: () async {
var weatherData = await weather.getLocationWeather();
updateUI(weatherData);
},
child: const Icon(
Icons.near_me,
size: 50.0,
),
),
TextButton(
// 도시 입력하는 페이지로 전달, typedName은 해당 페이지에서 pop 값을 받음
onPressed: () async {
var typedName = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CityScreen()));
if (typedName != null) {
var weatherData =
await weather.getCityWeather(typedName);
updateUI(weatherData);
}
},
child: const Icon(
Icons.location_city,
size: 50.0,
),
),
],
),
Padding(
padding: EdgeInsets.only(left: 15.0),
child: Row(
children: <Widget>[
Text(
'$temperature°',
style: kTempTextStyle,
),
Text(
weatherIcon,
style: kConditionTextStyle,
),
],
),
),
Padding(
padding: EdgeInsets.only(right: 15.0),
child: Text(
'$weatherMessage in $cityName',
textAlign: TextAlign.right,
style: kMessageTextStyle,
),
),
],
),
),
),
);
}
}
city_screen.dart
- Navigator.pop으로 도시 이름 전달
import 'package:flutter/material.dart';
import 'package:clima_flutter_flearner/utilities/constants.dart';
class CityScreen extends StatefulWidget {
@override
_CityScreenState createState() => _CityScreenState();
}
class _CityScreenState extends State<CityScreen> {
String? cityName;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('images/city_background.jpg'),
fit: BoxFit.cover,
),
),
constraints: const BoxConstraints.expand(),
child: SafeArea(
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: TextButton(
onPressed: () {
// 뒤로 버튼 눌렀을 때
Navigator.pop(context);
},
child: const Icon(
Icons.arrow_back_ios,
size: 50.0,
),
),
),
Container(
padding: const EdgeInsets.all(20.0),
child: TextField(
style: TextStyle(
color: Colors.black,
),
decoration: kTextFieldInputDecoration,
onChanged: (value) {
// 값이 입력될 때 cityName에 입력된 값 전달
cityName = value;
},
),
),
TextButton(
onPressed: () {
// 도시 이름까지 같이 전달
Navigator.pop(context, cityName);
print(cityName);
},
child: Text(
'Get Weather',
style: kButtonTextStyle,
),
),
],
),
),
),
);
}
}
반응형
'Programming > Flutter' 카테고리의 다른 글
플러터(Flutter) 스터디 6주차 (0) | 2021.11.13 |
---|---|
플러터 스터디 3주차 (0) | 2021.10.30 |
플러터(Flutter) 스터디 2주차 (0) | 2021.10.23 |
플러터(Flutter) 스터디 1주차 (0) | 2021.10.04 |