이번 카테고리는 google map 을 사용하는 방법에 대해서 알아보겠습니다.
개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1
소스코드 위치 -
화면은 아래와 같습니다.
메인화면에서 "Places Nearby" 버튼을 클릭하면 아래와 같은 화면이 나옵니다.
MapType 을 클릭하면 지도의 타입(normal, satellite, terrain, hybrid)을 순차적으로 변경됩니다.
"어디에서 볼까?" 를 클릭하면 베이커리, 레스토랑, 카페 를 선택할수 있습니다.
관련 패키지들은
google_maps_flutter: ^2.1.8
flutter_form_builder: ^7.3.1
form_builder_validators: ^8.1.1
http: ^0.13.4
API 관련 키 취득 및 등록은 구글의 절차대로 신청해서 받으시면 됩니다.
AndroidManifest.xml 파일 설정 방법은 아래의 유투브를 참고하시면 됩니다.
constants.dart
const String baseUrl =
'https://maps.googleapis.com/maps/api/place/nearbysearch/json';
const places = [
{'id': '1', 'placeName': '베이커리'},
{'id': '2', 'placeName': '레스토랑'},
{'id': '3', 'placeName': '카페'}
];
main.dart
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'constants/constants.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Google Maps Demo',
home: MapSample(),
);
}
}
class MapSample extends StatefulWidget {
const MapSample({Key? key}) : super(key: key);
@override
State<MapSample> createState() => MapSampleState();
}
class MapSampleState extends State<MapSample> {
final Completer<GoogleMapController> _controller = Completer();
// 5가지, non, normal, satellite, terrain, hybrid 등등.
MapType _googleMapType = MapType.normal;
// 클릭시마다 지도 타입이 변경, 4로 나눠서 나머지 값으로 지도 타입 설정
int _mapType = 0;
final Set<Marker> _markers = {};
//
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
@override
void initState() {
super.initState();
_markers.add(
const Marker(
markerId: MarkerId('myInitPosition'),
position: LatLng(37.53609444, 126.9675222),
infoWindow: InfoWindow(title: 'My Position', snippet: 'Where am I ?'),
),
);
}
final CameraPosition _initCameraPosition = const CameraPosition(
target: LatLng(37.53609444, 126.9675222),
zoom: 14,
);
void _onMapCreated(GoogleMapController controller) {
// 이제 이 콘트롤을 프로그램애서 사용준비 완료.
_controller.complete(controller);
}
// 지도타입 설정
void _changeMapType() {
setState(() {
_mapType++;
_mapType = _mapType % 4;
switch (_mapType) {
case 0:
_googleMapType = MapType.normal;
break;
case 1:
_googleMapType = MapType.satellite;
break;
case 2:
_googleMapType = MapType.terrain;
break;
case 3:
_googleMapType = MapType.hybrid;
break;
default:
_googleMapType = MapType.normal;
break;
}
});
}
void _submit() {
if (!_fbKey.currentState!.validate()) {
return;
}
_fbKey.currentState!.save();
final inputValues = _fbKey.currentState!.value;
final id = inputValues['placeId'];
final foundPlace = places.firstWhere(
(place) => place['id'] == id,
orElse: () => {}, //'id': 'null', 'placeName': 'null'
);
debugPrint(foundPlace.toString());
if(foundPlace['placeName'] == null){
Navigator.of(context).pop();
return;
} else {
Navigator.of(context).pop();
}
}
void _gotoGangnam() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15.0),
topRight: Radius.circular(15.0),
),
),
builder: (context) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
top: 40,
right: 20,
left: 20,
bottom: 20,
),
child: FormBuilder(
key: _fbKey,
child: Column(
children: <Widget>[
FormBuilderDropdown(
name: 'placeId', // map 의 키값
// attribute: 'placeId',
hint: const Text('어떤 장소를 원하세요?'),
decoration: const InputDecoration(
filled: true,
labelText: '장소',
border: OutlineInputBorder(),
),
validator: FormBuilderValidators.required(
errorText: '장소 선택은 필수입니다!',
),
items: places.map<DropdownMenuItem<String>>(
(place) {
return DropdownMenuItem<String>(
value: place['id'],
child: Text(place['placeName']!),
);
},
).toList(),
),
],
),
),
),
MaterialButton(
onPressed: _submit,
color: Colors.indigo,
textColor: Colors.white,
child: const Text('Submit'),
),
const SizedBox(height: 20),
],
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
GoogleMap(
mapType: _googleMapType,
initialCameraPosition: _initCameraPosition,
onMapCreated: _onMapCreated,
// myLocationEnabled: true,
markers: _markers,
),
Container(
margin: const EdgeInsets.only(top: 60, right: 10),
alignment: Alignment.topRight,
child: Column(
children: <Widget>[
FloatingActionButton.extended(
heroTag: 'btn1',
label: Text('$_googleMapType'),
icon: const Icon(Icons.map),
elevation: 8,
backgroundColor: Colors.red[400],
onPressed: _changeMapType,
),
const SizedBox(height: 10),
FloatingActionButton.extended(
heroTag: 'btn2',
label: const Text('어디에서 볼까?'),
icon: const Icon(Icons.zoom_out_map),
elevation: 8,
backgroundColor: Colors.blue[400],
onPressed: _gotoGangnam,
),
],
),
),
],
),
);
}
}
[참고자료] 헤비프랜
- https://www.youtube.com/watch?v=7Hlos9a70Hg&list=PLGJ958IePUyBeZRFKmL5NrZrYi0mPnNcq&index=3
'Flutter > 04 Widgets' 카테고리의 다른 글
[Flutter] Widgets - Google map 3(Place autocomplete) (0) | 2022.07.01 |
---|---|
[Flutter] Widgets - Google map 2 (0) | 2022.06.30 |
[Flutter] Widgets - Completer (0) | 2022.06.28 |
[Flutter] Widgets - FormBuilder 4 (0) | 2022.06.27 |
[Flutter] Widgets - FormBuilder 3 (0) | 2022.06.25 |