I am working on google maps and I am trying to drag my map(camera position) without changing marker. Marker should be at center and to pick the location when I stop dragging
This is my complete code of map functionality:
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFFFFFFF),
Color(0xFFF2F2F4),
Color(0xFFF2F2F4),
],
),
),
alignment: Alignment.center,
child: SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: true,
body: Stack(
alignment: Alignment.bottomCenter,
children: [
Stack(
children: [
GoogleMap(
initialCameraPosition: CameraPosition(
target: _initialCameraPosition, zoom: 13),
mapType: MapType.normal,
onMapCreated: _onMapCreated,
myLocationEnabled: true,
markers: _createMarker(),
onCameraMove: ((_position) => _updatePosition(_position)),
),
Padding(
padding:
EdgeInsets.symmetric(horizontal: 12.w, vertical: 12.h),
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Card(
shape: const CircleBorder(),
child: Padding(
padding: EdgeInsets.all(8.w),
child: Icon(
Icons.arrow_back,
color: Constants.colorTheme,
),
),
),
),
),
],
),
DraggableScrollableSheet(
initialChildSize: 0.4,
minChildSize: 0.4,
maxChildSize: 0.65,
builder: (context, controller) => Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(22.r)),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
controller: controller,
child: Padding(
padding: EdgeInsets.only(
left: 22.w, right: 22.w, bottom: 22.w, top: 10.h),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.center,
child: Container(
height: 5,
width: 50,
decoration: ShapeDecoration(
shape: const StadiumBorder(),
color: Colors.grey.withOpacity(0.3),
),
),
),
child: GestureDetector(
onTap: () async {
final sessionToken = const Uuid().v4();
var result = await showSearch(
context: context,
delegate: AddressSearch(sessionToken),
);
if (result != null) {
setState(() {
String address = '';
address = result.description;
strSearchedAddress = address;
log(result.lat.toString());
log(result.long.toString());
strLongitude = result.long.toString();
strLatitude = result.lat.toString();
_mapsController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
double.parse(strLatitude),
double.parse(strLongitude)),
zoom: 18),
),
);
_initialCameraPosition = LatLng(
double.parse(strLatitude),
double.parse(strLongitude));
_createMarker();
});
}
},
child: Row(
children: [
Expanded(
child: Row(
children: [
Icon(Icons.search_rounded,
color: Constants.colorBlack,
size: 18.w),
SizedBox(width: 10.w),
Text(
Languages.of(context)!
.labelSearchLocation,
style: TextStyle(
color: Constants.colorBlack,
fontFamily: Constants.appFont,
fontSize: 14.sp),
),
],
),
),
),
SizedBox(width: 12.w),
Text(
'OR',
style: TextStyle(
color: Constants.colorBlack,
fontFamily: Constants.appFont,
fontSize: 14.sp),
),
SizedBox(width: 12.w),
InkWell(
onTap: () async {
setState(() {
isAutoLocateLoading = true;
});
var locations = await Geocoder2
.getAddressFromCoordinates(
latitude: widget.currentLat ?? 0.0,
longitude: widget.currentLong ?? 0.0,
googleMapApiKey: apiKey,
);
setState(() {
strSearchedAddress = locations
.results.first.formattedAddress;
log(locations.results.first.geometry
.location.lat
.toString());
log(locations.results.first.geometry
.location.lng
.toString());
strLongitude = locations.results.first
.geometry.location.lng
.toString();
strLatitude = locations.results.first
.geometry.location.lat
.toString();
_mapsController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
double.parse(strLatitude),
double.parse(
strLongitude)),
zoom: 18),
),
);
_initialCameraPosition = LatLng(
double.parse(strLatitude),
double.parse(strLongitude));
_createMarker();
isAutoLocateLoading = false;
});
},
child: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: Constants.colorTheme,
borderRadius:
BorderRadius.circular(8.r),
),
width: 40.w,
height: 40.w,
child: isAutoLocateLoading
? CircularProgressIndicator(
strokeWidth: 2,
backgroundColor:
Constants.colorTheme,
valueColor:
const AlwaysStoppedAnimation(
Colors.white),
)
: Icon(
Icons.near_me_rounded,
color: Colors.white,
size: 18.w,
),
),
)
],
),
),
),
),
Visibility(
visible: strSearchedAddress.isNotEmpty,
child: SimpleShadow(
opacity: 0.6,
color: Colors.black12,
offset: const Offset(0, 3),
sigma: 5,
child: Container(
margin: EdgeInsets.only(top: 22.h),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: Colors.grey.withOpacity(0.2)),
),
child: Text(
'Selected Address: $strSearchedAddress',
style: TextStyle(
color: Colors.grey,
fontFamily: Constants.appFont),
),
),
),
),
SimpleShadow(
opacity: 0.6,
color: Colors.black12,
offset: const Offset(0, 3),
sigma: 5,
child: Container(
margin: EdgeInsets.only(top: 22.h),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: Colors.grey.withOpacity(0.2)),
),
child: Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
Languages.of(context)!.labelHouseNo,
style: TextStyle(
fontFamily: Constants.appFontBold,
fontSize: 16,
fontWeight: FontWeight.bold),
SimpleShadow(
opacity: 0.6,
color: Colors.black12,
offset: const Offset(0, 3),
sigma: 5,
child: Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
Languages.of(context)!.labelLandmark,
style: TextStyle(
fontSize: 18.sp,
fontFamily: Constants.appFontBold,
color: Constants.colorBlack,
),
),
),
SizedBox(height: 22.h),
TextField(
decoration: InputDecoration.collapsed(
hintText: Languages.of(context)!
.labelAnyLandmarkNearYourLocation,
border: InputBorder.none,
),
style: TextStyle(
fontFamily: Constants.appFont,
fontSize: 14.sp,
color: Constants.colorBlack,
SizedBox(height: 22.h),
Row(
children: [
Expanded(
child: CustomElevatedButton(
onPressed: () {
if (_textFullAddress.text.isNotEmpty) {
dialogShowDialog();
} else {
Constants.toastMessage(
'We are missing your complete address, please add one!');
}
},
buttonLabel: widget.isFromAddAddress
? Languages.of(context)!.labelAddAddress
: 'Set This & Proceed to Payment',
dialogShowDialog() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(22.r),
topRight: Radius.circular(22.r),
),
),
builder: (BuildContext context) {
return Padding(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Padding(
padding: EdgeInsets.all(22.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
Languages.of(context)!.labelAttachLabel,
style: TextStyle(
fontSize: 18.sp,
fontFamily: Constants.appFontBold,
),
),
GestureDetector(
child: const Icon(Icons.cancel_rounded,
color: Colors.redAccent),
onTap: () {
Navigator.pop(context);
},
)
],
),
Container(
margin: EdgeInsets.only(top: 22.h),
padding:
EdgeInsets.symmetric(vertical: 10.w, horizontal: 12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.grey.withOpacity(0.3)),
),
child: Row(
children: [
Icon(Icons.new_label_rounded,
color: Constants.colorTheme, size: 18.w),
SizedBox(width: 10.w),
Expanded(
child: TextField(
keyboardType: TextInputType.text,
controller: _textAddressLabel,
decoration: InputDecoration.collapsed(
hintText: Languages.of(context)!
.labelAddLabelForThisLocation,
border: InputBorder.none,
),
style: TextStyle(
fontFamily: Constants.appFont,
fontSize: 14.sp,
color: Constants.colorBlack,
),
),
),
],
),
),
SizedBox(height: 22.h),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.all(7.w),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.grey.withOpacity(0.3)),
),
child: Icon(
Icons.public_rounded,
color: Constants.colorTheme,
size: 20.w,
),
),
SizedBox(width: 12.w),
Expanded(
child: Text(
strSearchedAddress,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.sp,
fontFamily: Constants.appFont,
color: Constants.colorBlack),
),
)
],
),
SizedBox(height: 22.h),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(
Languages.of(context)!.labelCancel,
style: TextStyle(color: Constants.colorBlack),
),
),
SizedBox(width: 22.w),
TextButton(
onPressed: () {
if (strSearchedAddress.isEmpty) {
Constants.toastMessage(
Languages.of(context)!.labelPleaseSearchAddress);
} else if (_textAddressLabel.text.isEmpty) {
Constants.toastMessage(Languages.of(context)!
.labelPleaseAddLabelForAddress);
} else {
String strAddressLabel = _textAddressLabel.text;
if (strAddressLabel.trim().isNotEmpty) {
callAddUserAddress(strAddressLabel);
} else {
Constants.toastMessage(
'Please Add Label For Your Location');
}
}
},
child: Text(
'Save',
style: TextStyle(color: Constants.colorTheme),
),
Future<BaseModel<CommonResponse>> callAddUserAddress(strAddressLabel) async {
CommonResponse response;
try {
Constants.onLoading(context);
Map<String, String> body = {
'address': _textFullAddress.text.toString(),
'lat': strLatitude,
'lang': strLongitude,
'type': strAddressLabel,
};
response = await RestClient(RetroApi().dioData()).addAddress(body);
Constants.hideDialog(context);
log(response.success.toString());
if (response.success!) {
Navigator.pop(context);
Navigator.of(context).pushReplacement(
Transitions(
transitionType: TransitionType.slideUp,
curve: Curves.bounceInOut,
reverseCurve: Curves.fastLinearToSlowEaseIn,
widget: const ManageAddressesScreen(),
),
);
} else {
Constants.toastMessage(
Languages.of(context)!.labelErrorWhileAddAddress);
}
} catch (error, stacktrace) {
Constants.hideDialog(context);
log("Exception occurred: $error stackTrace: $stacktrace");
return BaseModel()..setException(ServerError.withError(error: error));
}
return BaseModel()..data = response;
}
void _updatePosition(CameraPosition _position) {
Position newMarkerPosition = Position(
latitude: _position.target.latitude,
longitude: _position.target.longitude,
accuracy: 10,
altitude: 1.0,
heading: 0.0,
speed: 10,
speedAccuracy: 1,
timestamp: null);
Marker marker = Marker(markerId: const MarkerId("marker_1"));
setState(() {
marker = marker.copyWith(
positionParam:
LatLng(newMarkerPosition.latitude, newMarkerPosition.longitude));
});
}
}
for a quick view to relevant parts
Maker method
Set<Marker> _createMarker() {
return <Marker>{
Marker(
draggable: true,
markerId: const MarkerId("marker_1"),
position: _initialCameraPosition,
icon: _markerIcon,
),
};
}
Initial value of map
@override
void initState() {
super.initState();
_markerIcon = widget.marker;
_initialCameraPosition = LatLng(widget.currentLat!, widget.currentLong!);
}
GoogleMap
GoogleMap(
initialCameraPosition: CameraPosition(
target: _initialCameraPosition, zoom: 13),
mapType: MapType.normal,
onMapCreated: _onMapCreated,
myLocationEnabled: true,
markers: _createMarker(),
onCameraMove: ((_position) => _updatePosition(_position)),
),
Update Position method
void _updatePosition(CameraPosition _position) {
Position newMarkerPosition = Position(
latitude: _position.target.latitude,
longitude: _position.target.longitude,
accuracy: 10,
altitude: 1.0,
heading: 0.0,
speed: 10,
speedAccuracy: 1,
timestamp: null);
Marker marker = Marker(markerId: const MarkerId("marker_1"));
setState(() {
marker = marker.copyWith(
positionParam:
LatLng(newMarkerPosition.latitude, newMarkerPosition.longitude));
});
}
On Map created method
void _onMapCreated(GoogleMapController controller) {
_mapsController = controller;
}
Ui view of map What I want(Video descrition) enter link description here
CodePudding user response:
Steps
- create a stack
- In children of stack place your google maps
- Second children of the stack should be your marker/icon, positioned at the center, you can use Positioned or Align widget.
- Use the predefined method of GoogleMaps widget {void Function(CameraPosition) onCameraMove} to get the latitude and longitude of the center of the maps. Now you can use the latitude and longitude in any way, maybe store it to firebase, maybe update the map, whatever you want.
Your code will look like this
class MyLocationClass extends StatefulWidget {
@override
_MyLocationClassState createState() => _MyLocationClassState();
}
class _MyLocationClassState extends State<MyLocationClass> {
double currentLatitude, currentLongitude;
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Stack(
children: [
GoogleMap(
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: LatLng(34.0060495, 71.5179581),
zoom: 14.4746,
),
markers: Set.of([]), //replace this line with your marker/s code.
onCameraMove: (cameraPosition) {
currentLongitude = cameraPosition.target.longitude; //gets the center longitude
currentLatitude = cameraPosition.target.latitude; //gets the center lattitude
},
),
Align(
alignment: Alignment.center,
child: IconButton(
iconSize: 30,
icon: Icon(
Icons.place,
color: Colors.red,
),
onPressed: () {
},
)
),
//a button if you want to use the saved location
Positioned(
bottom: MediaQuery.of(context).size.height * 0.02,
right: MediaQuery.of(context).size.width * 0.25,
left: MediaQuery.of(context).size.width * 0.25,
child: TextButton(
onPressed: ()
{
//save the currentLongitude, currentLatitude or do something else.
},
child: Text('Save'),
)),
],
),
));
}
}