package:location
이 Flutter 플러그인은 Android와 iOS에서 위치 정보를 가져오는 기능을 제공합니다. 또한 위치가 변경되면 콜백을 제공합니다.
앱에서 자신의 위치 정보를 표시하려면 어떻게 해야 할까? 네이티브(Kotlin)로 제작하여 위치 정보를 받아오기 위한 동의를 받고 실질적인 값을 받아오는 것도 하나의 방법일 수 있다. 하지만 여기에 더 쉽고 빠르게 위치 정보를 표시할 수 있게 도와주는 location 패키지가 있다.
location 패키지는 디바이스의 위치(위도, 경도 등)를 가져오거나 실시간 위치 추적을 구현할 수 있게 해주는 강력한 패키지로 이 패키지는 iOS와 Android 모두 지원하며, 위치 권한 요청과 설정 확인도 자동으로 처리해 준다.
일단 사용하기 위해선 먼저 프로젝트의 pubspec.yaml파일 안에 location패키지를 추가해야 할 것이다.

프로젝트에 추가해 준다.
flutter pub add location

pubspec.yaml 파일 안에 문제없이 들어갔다면 이제 바로 사용해 줄 건데 안드로이드에서 사용한다면 매니페스트(AndroidManifest.xml) 파일에 퍼미션을 추가 설정해줘야 한다.
매니페스트 파일을 일단 열어주자. 위치는 프로젝트이름/android/app/src/main/AndroidManifest.xml이다.

아래의 <uses-permission>을 최상단에 위치하고 있는 <manifest> 단에 넣어주자.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

이렇게 잘 들어갔다면 이제 안드로이드에서 위치 백그라운드 모드와 정보에 접근하고 필요한 권한을 사용할 준비가 끝난 것이다.
이제 진짜 사용해 보자,
먼저 package:location은 Location() 클래스를 제공해 준다. 이 클래스는 현재 기기의 지리적인 위치가 필요하거나 변경사항을 수신할 때 사용되게 된다.

하지만 위치가 표시되게 하기 위해서는 먼저 기기의 위치 서비스와 관련해서 사용자 자신의 앱에 위치 접근을 허용했는지 확인해야 한다. 다행히도 package:location은 몇 가지 방식으로 이 작업을 할 수 있게 만들어 주고 있다. (원래라면 코틀린 가서 권한 요청하고 막 난리를 떨어야 한다.)
그럼 위치 접근 허용을 위한 단계를 설명하겠다.
1. 위치 서비스 활성화 시키기
가장 먼저 위치 서비스를 활성화시켜줘야 한다.
그러기 위해선 먼저 location 객체를 하나 만들어줘야 할 것이다.
Location location = Location();
이 location객체를 생성함으로써 이 객체를 통해 GPS 기능 사용, 위치 요청, 권한 요청 등을 할 수 있게 된다.
그다음에 불리언 변수를 하나 만들어줘야 한다.
bool _serviceEnabled = false;
이 _serviceEnabled는 GPS(위치 서비스)가 켜져 있는지 여부를 담는 불리언 변수이다. 기본값은 꺼져있다는 것을 가정해서 false로 시작해주고 있다.
이제 서비스가 켜져 있는지에 대한 변수를 만들었으니 실질적으로 확인해 주면 되겠다.
_serviceEnabled = await location.serviceEnabled();
위처럼 사용하여 현재 위치 서비스(GPS)가 켜져 있는지 확인해 줄 수 있는데 await 키워드를 사용하여 비동기적으로 처리해줘야 한다.
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) return;
}
그다음엔 위치 서비스가 활성화되어 있지 않다면 사용자에게 requestService()를 요청해서 활성화시킬 수 있다.
근데 여전히 꺼져 있다면 더 이상 진행하지 않고 함수를 종료(return) 해 주면 된다.
2. 기기의 위치 파악하기
그다음은 기기의 위치를 파악하기 위한 방법이다. 먼저 위치 권한의 상태를 담는 변수를 하나 만들어줘야 한다.
late PermissionStatus _permissionGranted;
이제 이 변수에는 PermissionStatus.granted, PermissionStatus.denied 같은 enum 값이 들어오게 된다.
이렇게 late 키워드를 사용해서 나중에 꼭 초기화하도록 강제해 주자.
위치 권한의 상태를 담는 변수를 만들어 줬으니 바로 권한이 있는지 확인해 주자.
_permissionGranted = await location.hasPermission();
hasPermission()을 호출하여 현재 위치 권한이 있는지 확인해 준다.
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) return;
}
여기서 만약 권한이 없다면 사용자에게 requestPermission()을 호출해서 권한을 요청해 준다.
위와 마찬가지로 권한을 주지 않으면 종료해 준다.
이 작업들이 완료가 되었다면 마지막으로 getLocation을 호출해 주면 된다.
_currentLocation = await location.getLocation();
이것으로 권한과 위치 서비스가 모두 허용된 경우, 실제 현재 위치 정보를 가져올 수 있게 된다.
이제 동작들을 다 만들었으니 하나의 함수로 묶어서 iniState()에서 호출해 주자.
Location location = Location();
late LocationData _currentLocation; // 실제로 받아온 디바이스의 위치 정보를 담는 변수
bool _serviceEnabled = false;
late PermissionStatus _permissionGranted;
Future<void> _initLocation() async {
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) return;
}
_permissionGranted = await location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) return;
}
_currentLocation = await location.getLocation();
setState(() {});
}
@override
void initState() {
super.initState();
_initLocation();
// 여기서 _initLocation()을 호출해 앱 실행 시 바로 위치 정보를 가져오게 함.
}
이제 한번 앱을 실행해 보자. 실행해 보면 아래와 같이 정상적으로 권한 요청 팝업이 뜰 것이다.

동의해줬다면 이제 정상적으로 위치 정보를 불러올 수 있게 된다.
위치 정보는 내가 위의 코드에서 자연스럽게 넣어놨던 _currentLocation을 사용해서 가져와주면 된다.
late LocationData _currentLocation;
가져올 수 있는 값들은 아래와 같다.
주요 값
| 속성명 | 타입 | 설명 |
| latitude | double? | 위도 (북쪽 방향으로 얼마나 떨어져 있는지) |
| longitude | double? | 경도 (동쪽 방향으로 얼마나 떨어져 있는지) |
| accuracy | double? | 위치 정확도 (미터 단위) |
| altitude | double? | 고도 (해발 높이, 미터 단위) |
| speed |
double? | 속도 (m/s) |
| speedAccuracy | double? | 속도의 정확도 |
| heading | double? | 방향 (0~360도, 북쪽이 0) |
| time | double? | 위치 정보가 측정된 시간 (Unix timestamp, milliseconds) |
| isMock | bool? | 모의 위치인지 여부 (에뮬레이터나 개발자 모드에서 가짜 위치 제공 여부) |
| verticalAccuracy | double? | 고도의 정확도 |
| elapsedRealtimeNanos | int? | 위치 정보가 발생한 시간 (나노초 기준, Android 전용) |
| satelliteNumber | int? | 위치 측정에 사용된 위성 수 (Android에서만 제공, 드물게 사용) |
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_currentLocation.latitude.toString()),
Text(_currentLocation.longitude.toString()),
Text(_currentLocation.accuracy.toString()),
Text(_currentLocation.altitude.toString()),
Text(_currentLocation.speed.toString()),
Text(_currentLocation.speedAccuracy.toString()),
Text(_currentLocation.heading.toString()),
Text(_currentLocation.time.toString()),
Text(_currentLocation.isMock.toString()),
Text(_currentLocation.verticalAccuracy.toString()),
Text(_currentLocation.elapsedRealtimeNanos.toString()),
Text(_currentLocation.satelliteNumber.toString()),
],
)

현재 나는 에뮬레이터에서 실행하고 있는데 위치 정보를 가져와 보면 아래 지도와 같이 종로 쪽의 위치를 가져오고 있는 모습을 볼 수 있다.

이렇게 package:location의 사용법에 대해서 알아보았다. 근데 사실 이렇게 위치 정보만 받아오면 솔직히 의미 없는데 이 위치 패키지를 사용하여 사용자 기기의 위치 정보를 가져오고 거기에 google_maps_flutter 같은 패키지를 연동해서 사용하면 사용자의 위치 정보를 받아오고 그 정보를 바탕으로 지도에 표시해 주는 앱까지 만들 수 있을 것이다. 암튼 도움이 되었길 바라며 마치겠다.