map_latlng_picker 1.0.3
map_latlng_picker: ^1.0.3 copied to clipboard
A beautiful Google Maps location latitude and longitude picker with physics-based animations. Features an animated pin with bounce effects, elevation, and haptic feedback.
LatLng Picker #
A beautiful Flutter package that provides an interactive location latitude and longitude picker with physics-based animations for Google Maps. Pick locations with style using an animated pin that bounces and elevates with realistic physics.
What is it? #
LatLng Picker is a wrapper widget for Google Maps Flutter that adds an intuitive latitude and longitude picking experience. Instead of placing markers, users can pan the map while a fixed center pin shows where they're selecting. The pin features smooth animations with:
- Fixed center positioning
- Physics-based bounce effects
- Elevation animations with dynamic shadows
- Haptic feedback
- Fully customizable appearance
Under the Hood #
This package uses:
- google_maps_flutter - The official Google Maps plugin for Flutter
- Flutter's Animation Framework - For smooth, physics-based pin animations
- Custom AnimationControllers - To orchestrate bounce, stretch, and shadow effects
How to Use #
Installation #
Add to your pubspec.yaml:
dependencies:
map_latlng_picker: ^1.0.3
google_maps_flutter: ^2.14.0
Google Maps API Key Setup #
Before using this package, you need to configure Google Maps API keys for your target platforms:
π Web
Add to web/index.html:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY_HERE"></script>
π€ Android
Add to android/app/src/main/AndroidManifest.xml inside <application>:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY_HERE"/>
π iOS
In ios/Runner/AppDelegate.swift:
import GoogleMaps
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("YOUR_API_KEY_HERE")
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
π Get your Google Maps API key
Basic Usage #
Simply wrap your GoogleMap widget with LatLngLocationPicker:
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:map_latlng_picker/map_latlng_picker.dart';
class LocationPickerScreen extends StatefulWidget {
@override
State<LocationPickerScreen> createState() => _LocationPickerScreenState();
}
class _LocationPickerScreenState extends State<LocationPickerScreen> {
final LatLngLocationPickerController _controller = LatLngLocationPickerController();
LatLng? _selectedLocation;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Pick Location')),
body: LatLngLocationPicker(
controller: _controller,
enabled: true,
onLocationPicked: (location) {
setState(() {
_selectedLocation = location;
});
print('Selected: ${location.latitude}, ${location.longitude}');
},
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(37.7749, -122.4194),
zoom: 14,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_controller.isEnabled
? _controller.disable?.call()
: _controller.enable?.call();
},
child: Icon(_controller.isEnabled ? Icons.check : Icons.location_on),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Advanced Usage with Custom Pin #
LatLngLocationPicker(
enabled: true,
pinOffset: 60,
useHapticFeedback: true,
onLocationPicked: (location) {
// Handle picked location
},
pinWidget: AnimatedLocationPin(
state: _isPanning ? PinState.elevated : PinState.idle,
color: Colors.blue,
innerColor: Colors.white,
size: 50,
stickHeight: 25,
shadowDistance: 20,
),
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(37.7749, -122.4194),
zoom: 14,
),
),
)
Simplified Pin Customization with AnimatePinData #
LatLngLocationPicker(
enabled: true,
pinData: AnimatePinData(
color: Colors.blue,
innerColor: Colors.white,
stickColor: Colors.blueAccent,
size: 50,
stickHeight: 25,
stickBorderRadius: 4.0,
shadowColor: Colors.black54,
shadowDistance: 10.0,
),
onLocationPicked: (location) {
// Handle picked location
},
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(37.7749, -122.4194),
zoom: 14,
),
),
)
Parameters #
LatLngLocationPicker #
| Parameter | Type | Default | Description |
|---|---|---|---|
child |
Widget |
required | The GoogleMap widget to wrap |
controller |
LatLngLocationPickerController? |
null |
Controller to enable/disable picker programmatically |
onLocationPicked |
OnLatLngLocationPicked? |
null |
Callback fired when user releases the map |
pinWidget |
Widget? |
null |
Custom pin widget (uses AnimatedLocationPin by default) |
pinData |
AnimatePinData? |
null |
Pin customization data (alternative to pinWidget) |
pinOffset |
double |
50 |
Vertical offset from center (useful for pins with bottom pointer) |
enabled |
bool |
false |
Whether location picker is enabled by default |
useHapticFeedback |
bool |
true |
Enable haptic feedback on pan start/end |
AnimatePinData #
| Parameter | Type | Default | Description |
|---|---|---|---|
state |
PinState? |
null |
Advanced mode: Control pin state |
isElevated |
bool |
false |
Simple mode: Whether pin is elevated |
color |
Color |
required | Main circle color |
innerColor |
Color |
required | Inner dot color |
stickColor |
Color? |
null |
Stick color (defaults to main color) |
stickWidth |
double? |
null |
Stick width (defaults to 15% of size) |
stickBorderRadius |
double |
4.0 |
Border radius of the stick |
shadowColor |
Color |
required | Shadow color |
size |
double |
40.0 |
Pin size (circle diameter) |
stickHeight |
double |
20.0 |
Height of the stick |
shadowDistance |
double |
10.0 |
Shadow distance when elevated (advanced mode) |
duration |
Duration |
Duration(milliseconds: 300) |
Animation duration |
AnimatedLocationPin #
| Parameter | Type | Default | Description |
|---|---|---|---|
state |
PinState? |
null |
Advanced mode: Control pin state (idle/elevated) |
isElevated |
bool |
false |
Simple mode: Whether pin is elevated |
color |
Color |
Colors.red |
Main circle color |
innerColor |
Color |
Colors.white |
Inner dot color |
stickColor |
Color? |
null |
Stick color (defaults to main color) |
stickWidth |
double? |
null |
Stick width (defaults to 10% of size) |
stickBorderRadius |
double |
12 |
Border radius of the stick |
shadowColor |
Color |
Colors.black26 |
Shadow color |
size |
double |
40 |
Pin size (circle diameter) |
stickHeight |
double |
20 |
Height of the stick |
shadowDistance |
double |
15 |
Shadow distance when elevated (advanced mode) |
duration |
Duration |
Duration(milliseconds: 200) |
Animation duration (simple mode) |
LatLngLocationPickerController #
| Method/Property | Type | Description |
|---|---|---|
enable() |
VoidCallback? |
Enable location picking mode |
disable() |
VoidCallback? |
Disable location picking mode |
isEnabled |
bool |
Get current enabled state |
dispose() |
void |
Clean up controller resources |
Pin Animation Modes #
Simple Mode (Boolean) #
Use isElevated for basic on/off elevation:
AnimatedLocationPin(
isElevated: _isPanning,
)
Advanced Mode (PinState) #
Use state for enhanced animations with smoother transitions:
AnimatedLocationPin(
state: _isPanning ? PinState.elevated : PinState.idle,
)
Examples #
Example 1: Basic Location Picker #
LatLngLocationPicker(
enabled: true,
onLocationPicked: (location) {
print('Location: ${location.latitude}, ${location.longitude}');
},
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(0, 0),
zoom: 10,
),
),
)
Example 2: With Controller #
final controller = LatLngLocationPickerController();
// Enable picker
controller.enable?.call();
// Disable picker
controller.disable?.call();
// Check state
if (controller.isEnabled) {
// Picker is active
}
Example 3: Custom Styled Pin #
LatLngLocationPicker(
enabled: true,
pinWidget: AnimatedLocationPin(
isElevated: _isPanning,
color: Colors.purple,
innerColor: Colors.yellow,
size: 60,
stickHeight: 30,
stickBorderRadius: 4,
),
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(37.7749, -122.4194),
zoom: 14,
),
),
)
Example 4: Using AnimatePinData #
LatLngLocationPicker(
enabled: true,
pinData: AnimatePinData(
color: Colors.green,
innerColor: Colors.white,
stickColor: Colors.greenAccent,
size: 55,
stickHeight: 28,
stickBorderRadius: 6.0,
shadowColor: Colors.black45,
shadowDistance: 12.0,
),
onLocationPicked: (location) {
print('Picked: ${location.latitude}, ${location.longitude}');
},
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(37.7749, -122.4194),
zoom: 14,
),
),
)
Features #
- Smooth physics-based animations
- Customizable pin appearance
- Haptic feedback support
- Controller for programmatic control
- Works as a simple wrapper - no map recreation needed
- Preserves all GoogleMap properties and callbacks
- Two animation modes (simple boolean and advanced state-based)
- Dynamic shadow effects
Author #
Paul Jeremiah
Twitter: @edeme_kong
Support #
If you find this package helpful, consider buying me a coffee! β
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
