들어가기 앞서...
구글 맵과 geolocator 플러그인을 통해서 회사의 위치와 나의 위치를 알고 어떤 조건에 맞으면 출근이 가능하게끔 하는 프로젝트이다.
본문으로...
1. 기본 세팅!
geolocator | Flutter Package
Geolocation plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API for generic location (GPS etc.) functions.
pub.dev
google_maps_flutter | Flutter Package
A Flutter plugin for integrating Google Maps in iOS and Android applications.
pub.dev
1) 구글 맵 api readme 따라 해주자!
홈페이지 따라가서 계정 설정 다한 후에 사용 설정된 api 에 저 2개 sdk 가 잘 설정 되어있는지 확인!
그리고 api key 복사 하기
안드로이드 세팅
1) android > app > src > build.gradle
android {
defaultConfig {
minSdkVersion 20
}
}
2) androif > app > src> main > AndroidManifest.xml
<manifest ...
<application ...
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="YOUR KEY HERE"/>
3) ios/Runner/AppDelegate.swift
import UIKit
import Flutter
import GoogleMaps
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("YOUR KEY HERE")
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
depenencies 추가
google_maps_flutter: ^2.2.2
geolocator: ^9.0.2
geolocator 세팅
android 설정!
androidx 는 최근 플러터에서는 자동으로 설정되어있으므로 넘어간다.
현재 지금 필요한 permission 은 ACCESS_FINE_LOCATION 만 복사해서 적용해주자! (AndroidManifext.xml 파일에 적용!)
// 현재 정보를 접근이 가능하게 하는 permission (굉장히 디테일한 위치 접근가능)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
// 조금 더 덜 정확한 위치
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
// 예시) 자전거 얼마나 탔는지 기록해주는 앱 에서 사용한다.
// 앱이 백그라운드에 있을때(현재 앱이 실행중이 아니여도) 현재의 위치를 가져올 수 있는 기능
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
ios 세팅!
안드로이드 유저는 생소하지만, 애플 유저들은 어느 앱을 실행할때마다 권한을 요청하는 부분이 있다.
- 앱의 위치 권한을 항상 허용할것인가?
- 앱을 사용하는 동안 권한을 허용할것인가?
- 권한을 허용하지 않는다.
라는 3가지 선택사항을 항상 봐왔을 것이다.
// 앱을 사용하는 동안 허용!
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
// 항상 허용!
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location when in the background.</string>
// 만약 앱 실행시 에러가 난다면, cleans 하고 앱을 다시 실행해주면 된다.
flutter clean
2. 구글 지도를 띄워보자!
1) 구글 샘플을 한번 따라해보자
샘플을 그대로 복사해서 실행을 해보면, 위도와 경
도에 맞게 지도가 나오고 To the lake! 버튼이 작동되는 것을 볼 수 있다.
// 컨트롤러 에서 업데이트하면서 반환된 값을 저장할 변수 선언
final Completer<GoogleMapController> _controller =
Completer<GoogleMapController>();
// 앱이 처음 실행될때 보여지는 위치를 담는 변수
static const CameraPosition _kGooglePlex = CameraPosition(
// 경도와 위도
target: LatLng(37.42796133580664, -122.085749655962),
// 줌 레벨 (확대를 어느정도로 할 것인지,숫자가 높을 수록 확대)
zoom: 14.4746,
);
// 강의 위치를 담는 변수
static const CameraPosition _kLake = CameraPosition(
// bearing :카메라의 방위를 뜻한다. 기본적으로 0.0(북쪽)을 가르킨다.90 은 동쪽, 192는 남쪽인데, 살짝 서쪽이다.
bearing: 192.8334901395799,
target: LatLng(37.43296265331129, -122.08832357078792),
tilt: 59.440717697143555,
zoom: 19.151926040649414);
@override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
// MapType 은 enum 열거형으로 작성되어있다. 지도가 보여지는 타입이다. (none, noramal, satellite,terrain, hybrid 가 존재)
mapType: MapType.hybrid,
// initialCameraPosition 초기 카메라 위치
initialCameraPosition: _kGooglePlex,
// onMapCreated : 구글맵을 위한 구글맵 컨트롤러를 받는데 사용한다.
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
// 오른쪽 하단 모서리에 body 위에 표시되는 버튼이다.
floatingActionButton: FloatingActionButton.extended(
// 클릭하면 _goToTheLake 함수 실행
onPressed: _goToTheLake,
// 플러팅 버튼에 써져있는 글씨
label: const Text('To the lake!'),
// 플러팅 버튼의 아이콘
icon: const Icon(Icons.directions_boat),
),
);
}
// 비동기 함수
Future<void> _goToTheLake() async {
// 구글 맵의 컨트롤러 생성
final GoogleMapController controller = await _controller.future;
// animateCamera() : 지도 카메라의 위치의 애니메이션 변경을 시작한다.
// 플랫폼 쪽에 변경이 시작된후 반환된 [Future]가 완료된다.
// CameraUpdate : 카메라이동을 정의한다. 절대적인 이동과 상대적인 이동을 지원한다.
// newCameraPosition : 카메라를 지정된 위치로 이동하는 카메라 업데이틑 리턴한다.
controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));
}
2) 샘플 코드를 토대로 코딩!
// state 클래스
// latitude - 위도, longitude - 경도
// 현재 위치를 저장하는 방법
// 구글에서 제공하는 클래스 LatLng
static final LatLng companyLatLng = LatLng(37.518820573402, 126.89986969097);
// 줌 레벨
static final CameraPosition initialPosition = CameraPosition(
target: companyLatLng,
zoom: 15,
);
body: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: initialPosition,
),
3. 구글 맵 꾸미기!
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'오늘도 출근',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w700
),
),
),
body: Column(
children: [
Expanded(
flex: 2,
child: GoogleMap(
// 처음 실행시켰을때 기본 위치
initialCameraPosition: initialPosition,
mapType: MapType.normal,
),
),
Expanded(child: Text('출근'))
],
),
);
}
3. 코드 정리!
1) AppBar 분리
AppBar renderAppBar(){
return AppBar(
backgroundColor: Colors.white,
title: Text(
'오늘도 출근',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w700
),
),
);
}
2) 두개의 위젯을 새로 분리
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: renderAppBar(),
body: Column(
children: [
_CustomGoogleMap(initialPosition: initialPosition,),
_AttendanceCheckButton(),
],
),
);
}
class _CustomGoogleMap extends StatelessWidget {
final CameraPosition initialPosition;
const _CustomGoogleMap({Key? key, required this.initialPosition}) : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
flex: 2,
child: GoogleMap(
// 처음 실행시켰을때 기본 위치
initialCameraPosition: initialPosition,
mapType: MapType.normal,
),
);
}
}
class _AttendanceCheckButton extends StatelessWidget {
const _AttendanceCheckButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(child: Text('출근'));
}
}
4. 위치 권한 받는 기능 제작!
geolocator 플러그인 을 통해서 권한을 요청할것이다.
1) 함수 생성
Future<String> checkPermission() async {
// 로케이션 서비스(위치 여부)가 활성화 되어있는지 나타내는 함수, 비동기로 boolean 값 리턴
final isLocationEnabled = await Geolocator.isLocationServiceEnabled();
if (isLocationEnabled) {
return '위치 서비스를 활성화 해주세요.';
}
// LocationPermission : enum(열거형)
LocationPermission checkedPermission = await Geolocator.checkPermission();
// 만약 권한을 리턴받아 저장한 값이 == denied 라면?
if (checkedPermission == LocationPermission.denied) {
// 다시 요청해서 변수에 저장을 하고,
checkedPermission = await Geolocator.requestPermission();
// 저장을 한 후에도 변수의 값이 denied 라면 리턴문 출력
if (checkedPermission == LocationPermission.denied) {
return '위치 권한을 허가해주세요.';
}
// 리턴 받은 값 == deniedForever 이라면? 사용자가 직접 세팅에서 설정해야한다.
if (checkedPermission == LocationPermission.deniedForever) {
return '앱의 위치 권한을 세팅에서 허가해주세요.';
}
}
return '위치 권한이 허가되었습니다.';
}
1-1) LocationPermission 을 자세히 보기!
// 가능한 위치 권한을 나타낸다.
enum LocationPermission {
// defulat 상태이다.
// 장치 위치에 액세스할 수 있는 권한이 거부되었다. 앱을 다시 시도해야 한다.
denied,
// 장치 위치에 액세스할 수 있는 권한이 영구적으로 거부되었다.
// 권한 요청, 권한 대화 상자는 사용자가 앱 설정에서 권한을 업데이트할 때까지 표시가 되지 않는다.
// 플러터에서 권한을 다시 요청할 수가 없다. 그러니 사용자가 직접 설정에서 컨트롤 해줘야한다.
deniedForever,
// 앱이 사용중일 떄만 장치 위치에 액세스할 수 있는 권한이 허용된다.
whileInUse,
// 앱이 백그라운드에서 실행 중일때 장치 위치에 대한 액세스 권한이 허용된다.
always,
// 권한 상태를 확인할 수 없다.
// geolocator.checkPermission 메소드가 리턴했다.
// 권한 API를 구현하지 않는 브라우저인 경우 홈페이지를 참조해라 (https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API))
unableToDetermine
// 핸드폰인 경우, 위치 서비스를 허용 안 해주는 휴대폰은 없다.
// 컴퓨터에서 위치 서비스를 안해주는 것도 있다.
}
2) 함수 사용
갑자기 에러 발생!
해결 방법은 이 게시글을 확인하자 https://hitang.tistory.com/161
ERROR:D8: Cannot fit requested classes in a single dex file (# methods: 70980 > 65536)
FutrueBuilder 사용해서 api 통신 전, 후 그리기!
FutrueBuilder<String>(
future: checkPermission(),
buildr: (BuildContext context, AsyncSnapshot snapshot) {
// snapShot 으로 api 통신이 waiting 인지 done인지 알 수 있다.
// 현재 api 통신이 waiting 이라면, 로딩인디케이터를 통해 로딩 화면 보여주기
if(snapShot.connectionState == ConectinState.waiting) {
return Center(
child: CircularProgressIndicator(),
)
}
// snapShot의 data 가 내가 정한 ''과 같을경우 return 문(구글 지도와 현재 나의 위치 마커 등) 출력한다.
if(snapShot.data == '위치 권한이 허가되었습니다.') {
return ;
}
// 만약 둘다 아니라면, snapShot.data 출력
return Center(
child: Text(snapShot.data),
)
}
)

'⭐️ 개발 > 플러터' 카테고리의 다른 글
[이론] FutureBuilder 와 StreamBuilder (0) | 2022.12.20 |
---|---|
[에러] 갑자기 멀티덱스를 활성화라고 에러가 뜬다면? (4) | 2022.12.16 |
[프로젝트] 영상플레이어 (4) 동영상 위 버튼 올리기 (2) | 2022.12.15 |
[이슈] - 이미지가 출력이 안될때! (0) | 2022.12.14 |
[프로젝트] 영상플레이어 (1) HomeScreen 구현하기 (0) | 2022.12.13 |