[Flutter]Naver Map 사용하기
- -
오늘은 Naver Map을 활용해 개발 중인 애플리케이션에 지도를 적용해 보았습니다.
작업 과정에서는 pub.dev의 flutter_naver_map 라이브러리를 참고했으며, 문서가 잘 정리되어 있어 해당 문서를 참고하시면 도움이 될 것입니다.
※ 테스트 환경
- Flutter 3.22.3
- Dart 3.5.4
- flutter_naver_map 1.3.0
1. 어플리케이션 등록
1-1. Naver Map 사용 등록
NCP Console에 접속하여 Application을 등록합니다.
1-2. 네이버 지도 서비스 등록
Web Dynamic Map, Mobile Dynamic Map을 선택하고, Android, iOS 패키지명을 입력합니다.
1-3. Client ID 확인
2. Naver Map 위젯 만들기
2-1. 지도 초기화
.env 환경 변수에 생성된 Client ID를 등록합니다.
.env.local(.env)
NAVER_CLIENT_ID=클라이언트키 입력
앱의 시작점인 main() 함수에서 runApp이 실행되기 전 Naver Map을 초기화합니다. 여기서는 main() 함수에서 초기화를 했지만 정확하게는 지도 실행 전에 언제라도 상관이 없다고 가이드 문서에 명시되어 있습니다.
void main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
// 구성 파일 로드
await dotenv.load(fileName: kDebugMode ? ".env.local" : ".env");
// Naver Map 초기화
await NaverMapSdk.instance.initialize(
clientId: dotenv.get('NAVER_CLIENT_ID'),
onAuthFailed: (error) {
logger.e('Auth failed: $error');
});
runApp(const ProviderScope(child: MyApp()));
}
2-2. 지도 위젯 생성
지도를 어디서든 사용할 수 있게 공통 위젯으로 분리시켰습니다.
class MyNaverMap extends ConsumerStatefulWidget {
final double addX;
final double addY;
const MyNaverMap({
super.key,
required this.addX,
required this.addY,
});
@override
ConsumerState<MyNaverMap> createState() => _WeddingMain();
}
class _WeddingMain extends ConsumerState<MyNaverMap> {
NaverMapController? _mapController;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final cameraUpdate = NCameraUpdate.withParams(
target: NLatLng(widget.addY, widget.addX),
zoom: 12,
bearing: 0,
);
final marker =
NMarker(id: 'test', position: NLatLng(widget.addY, widget.addX));
// 지도 생성 이후 다시 호출되었을 때를 지도 좌표가 변경된 것으로 보고 마커와 위치를 이동시킨다.
if (_mapController != null) {
_mapController!.updateCamera(cameraUpdate);
_mapController!.addOverlay(marker);
}
return Container(
height: 250,
margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadiusRate),
border: Border.all(
width: 1.0,
color: borderColor,
),
color: basicBoxColor,
),
child: NaverMap(
options: NaverMapViewOptions(
mapType: NMapType.basic,
initialCameraPosition: NCameraPosition(
target: NLatLng(widget.addY, widget.addX),
zoom: 12,
bearing: 0,
tilt: 0),
),
forceGesture: false,
onMapReady: (controller) async {
_mapController = controller;
controller.addOverlay(marker);
},
onMapTapped: (point, latLng) {},
onSymbolTapped: (symbol) {},
onCameraChange: (position, reason) {},
onCameraIdle: () {},
onSelectedIndoorChanged: (indoor) {},
));
}
}
addX(경도), addY(위도) 를 인자값으로 전달받아 NaverMapController를 통해 지도의 카메라, 마커 위치를 재조정할 수 있도록 했습니다.
최초 지도가 호출될 때(onMapReady 실행전)에는 _mapController가 null로 존재하기 때문에 initailCameraPosition을 통해 카메라 위치가 셋팅됩니다. onMayReady 이후에 마커가 표기 되며, 지도 위젯이 재호출 되는 경우 _mapController를 통해 인자값으로 전달받은 위도, 경도로 카메라 위치와 마커를 수정합니다.
if (_mapController != null) {
_mapController!.updateCamera(cameraUpdate); _mapController!.addOverlay(marker);
}
생성된 지도 위젯을 사용해보겠습니다.
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyTextbox(textField: '주소', fontSize: fontSmallSize),
Container(
margin: const EdgeInsets.only(top: 5),
child: Column(
children: [
Container(
height: 45,
color: widgetWhiteColor,
child: TextField(
controller: _addressController,
readOnly: true,
decoration: const InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: borderColor, width: 1),
borderRadius: BorderRadius.all(
Radius.circular(borderRadiusRate)),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(borderRadiusRate)),
),
contentPadding: EdgeInsets.only(
left: 10,
),
hintStyle: TextStyle(
fontSize: fontMediumSize,
fontWeight: FontWeight.w500,
color: grayFontColor,
),
hintText: '예식장 주소',
),
style: const TextStyle(
color: widgetBlackColor,
fontSize: fontMediumSize,
fontWeight: FontWeight.w600,
),
onTap: () async {
await showTopModalSheet<String?>(
context,
MapWebview(
saveMap: (String value) {
var data = jsonDecode(value);
logger.i(data);
String address = data['address'];
double addX = data['lng'];
double addY = data['lat'];
ref
.read(weddingProvider(widget.cardNo).notifier)
.updateAddress(address, addX, addY);
},
),
);
},
),
),
// 지도 영역
MyNaverMap(
addX: addX,
addY: addY,
),
],
),
),
],
),
addX(경도), addY(위도)는 네이버 지도의 초기 위도, 경도값으로 초기화합니다.
DB에 저장된 값을 통해 지도가 그려지고(없는 경우 초기값) TextField의 onTab을 통해 호출된 웹 뷰(네이버 맵, 주소 검색)를 통해 전달받은 위도, 경도로 지도를 다시 그려지게 설계했습니다.
3. 테스트
의도한 바와 같이 작동하는지 확인해봅니다.
예제에 사용된 옵션 외에도 다양한 옵션이 존재합니다. 본 예제에서는 기본적인 지도의 생성과 사용법에 대해 알아봤고, 추가적인 옵션과 사용법은 아래 문서를 통해 확인하시길 바랍니다.
참고문서
'Framework > Flutter' 카테고리의 다른 글
[Error]Flutter 설치 에러 모음 (0) | 2024.12.18 |
---|---|
[Error]error: Sandbox: rsync.samba(22953) deny(1) file-write-create ... (0) | 2024.08.26 |
[Flutter]무한 스크롤 구현하기 (0) | 2024.06.21 |
[Flutter]iOS 앱 기본 언어 정보 한글로 변경하기 (0) | 2024.05.20 |
[Error]"Uncaught TypeError: Cannot read property 'cancel' of undefined" (0) | 2023.12.28 |
소중한 공감 감사합니다.