[플러터] 위치 기반 앱 만들기
이전 내용
[플러터] 비디오 플레이어 앱 만들기
이전 내용 [플러터] 주사위 앱 만들기이전 내용 [플러터] D-Day 앱 만들기 (feat. 폰트 반영하기, Cupertino 위젯)이전 내용 [플러터] 이미지 롤링 기능 구현하기이전 내용 [플러터] 웹 앱 만들어보기에
puppy-foot-it.tistory.com
위치 기반 앱
1. 사전 작업: 구글 지도 API 키 발급 받기
구글 지도를 사용해서 위치 기반 앱을 구현해 본다.
먼저, 구글 지도를 사용하기 위해서는 구글 클라우드 플랫폼에서 구글 지도 API를 발급받아야 한다.
https://cloud.google.com/
cloud.google.com
구글에 로그인 후 콘솔 클릭
프로젝트를 누르고, 새 프로젝트 클릭
프로젝트 이름을 입력하고 만들기 클릭
검색 창에 'Maps SDK for' 를 검색하고, 결과에 나온 for Android와 for iOS를 각각 클릭하여
[사용] 버튼 클릭 (iOS 도 같은 방법으로)
그리고나서, '키 및 사용자 인증 정보' 에서 Maps SDK for Android 를 누른 후 [+ 사용자 인증 정보 만들기] - [API 키] 를 누르면 API가 생성된다.
iOS 도 같은 방식으로 진행하며, API 키는 잘 보관해 둔다.
안드로이드 스튜디오 설정
앞서 발급받은 API 를 사용하기 위해 pubspec.yaml 에 플러그인을 추가해줘야 한다. dependencies 에 추가해 주면 된다.
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
google_maps_flutter: 2.9.0
geolocator: 13.0.1
※ geolocator 플러그인: 지리와 관련된 기능을 쉽게 사용할 수 있는 플러그인
- 위치 서비스 사용 권한 확인 후 권한 요청
- 현재 GPS 위치가 변경될 때마다 현재 위치의 값을 받을 수 있는 기능 사용
- 현재 위치와 목적지 간의 거리 계산
https://pub.dev/packages/geolocator
geolocator | Flutter package
Geolocation plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API for generic location (GPS etc.) functions.
pub.dev
그리고 안드로이드 설정을 위해 AndroidManifest.xml 에 권한을 등록하고 발급받은 구글 지도 API 키를 등록해 준다.
- 상세 위치 권한 등록
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- API 키 등록 (Maps SDK for Android)
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY" />
앱 구현하기
레이아웃은 아래와 같이 3등분으로 분할시킬 예정이다.
- AppBar: 타이틀 표시
- Body: 구글 지도 표시
- Footer: 길 찾기 표시 기능 구현
폴더 구조 및 코드
이번 앱은 [screen] 폴더 내에 있는 'home_screen.dart' 파일 내에서 대부분의 코드가 작성되었다.
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
class HomeScreen extends StatelessWidget {
static final LatLng goalLatLng = LatLng( // 지도 초기화 위도, 경도
37.5233273,
126.921252,
);
// 목적지 위치 마커 선언
static final Marker marker = Marker(
markerId: MarkerId('goal'),
position: goalLatLng,
);
// 위치 반경 표시
static final Circle circle = Circle(
circleId: CircleId('locationCircle'),
center: goalLatLng, // 원의 중심이 되는 위치 (위도, 경도 제공)
fillColor: Colors.blue.withOpacity(0.5), // 원 색상
radius: 100, // 원의 반지름 (m)
strokeColor: Colors.blue, // 원 테두리 색
strokeWidth: 1, // 원 테두리 두께
);
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: renderAppBar(),
body: FutureBuilder<String>(
future:checkPermission(),
builder:(context,snapshot) {
// 로딩 상태
if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
// 권한 허가 완료 상태
if(snapshot.data == '위치 권한이 허가되었습니다'){
return Column(
children: [
Expanded(
flex: 2,
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: goalLatLng,
zoom: 16, // 확대 정도 (높을수록 확대)
),
myLocationEnabled: true, // 내 위치 지도에 표시
markers: Set.from([marker]), // Set 로 Marker 제공
circles: Set.from([circle]), // Set 로 Circle 제공
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon( // 시계 아이콘
Icons.timelapse_outlined,
color: Colors.blue,
size: 50.0,
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: () async{
final curPosition = await Geolocator.getCurrentPosition(); // 현재 위치
final distance = Geolocator.distanceBetween(
curPosition.latitude, // 현재 위치 위도
curPosition.longitude, // 현재 위치 경도
goalLatLng.latitude, // 목적지 위치 위도
goalLatLng.longitude, // 목적지 위치 경도
);
bool canCheck =
distance < 100; // 100 미터 이내에 있으면 가능
showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Text('길 찾기'),
// 가능 여부에 따라 다른 메시지 제공
content: Text(
canCheck? '목적지를 찾으시겠습니까?' : '찾을 수 없는 위치입니다.',
),
actions: [
TextButton(
// 취소 누르면 false 반환
onPressed: () {
Navigator.of(context).pop(false);
},
child: Text('취소'),
),
if (canCheck) // 길 찾기 가능한 상태일 때만 [길 찾기] 버튼 제공
TextButton(
// 길 찾기 누르면 true 반환
onPressed: () {
Navigator.of(context).pop(true);
},
child: Text('길 찾기'),
),
],
);
},
);
},
child: Text('길 찾기!'),
),
],
),
),
],
);
}
// 권한 없는 상태
return Center(
child: Text(
snapshot.data.toString(),
),
);
}
),
);
}
AppBar renderAppBar() {
// AppBar 구현 함수
return AppBar(
centerTitle: true,
title: Text(
'길 찾기 앱',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w700,
),
),
backgroundColor: Colors.white,
);
}
Future<String> checkPermission() async {
final isLocationEnabled = await Geolocator.isLocationServiceEnabled();
// 위치 서비스 활성화 여부 확인
if (!isLocationEnabled) { // 위치 서비스 활성화 안 된 경우
return '위치 서비스를 활성화해주세요';
}
// 위치 권한 확인
LocationPermission checkedPermission = await Geolocator.checkPermission();
if (checkedPermission == LocationPermission.denied) { // 위치 권한 거절될 경우
// 위치 권한 요청
checkedPermission = await Geolocator.requestPermission();
if (checkedPermission == LocationPermission.denied) {
return '위치 권한을 허가해주세요';
}
}
// 위치 권한 거절 (앱에서 재요청 불가)
if (checkedPermission == LocationPermission.deniedForever) {
return '앱의 위치 권한을 설정에서 허가해주세요';
}
// 모든 조건 통과되면 위치 권한 허가 완료
return '위치 권한이 허가되었습니다.';
}
}
앱 실행 테스트
◆ 애뮬레이터 현재 위치 설정
애뮬레이터에는 GPS가 없어 현재 위치가 보이지 않으므로, GPS 위치를 임의로 설정해 줘야한다.
애뮬레이터의 오른쪽 점 세개 버튼을 누르면
아래와 같이 Extended Controls가 뜬다.
[참고]
코드팩토리의 플러터 프로그래밍
다음 내용