Location Picker Plus
A powerful Flutter plugin for address entry with Google Places API integration, featuring smart autocomplete, field locking, and comprehensive address parsing. Perfect for forms requiring complete address information.
🚀 v3.1.0 - GPS, Dropdowns & Individual Fields
✨ Single Powerful Widget for Complete Address Entry
CustomStreetAddressField - An all-in-one address entry widget with:
- 🔍 Google Places Autocomplete - Real-time address search (India & US)
- 📍 GPS Location Detection - One-click current location detection (works WITHOUT Google Places API)
- 🗺️ Complete Address Parsing - Auto-fills street, city, state, country, postal code
- 🔒 Smart Field Locking - Prevents editing when address from Google Places or GPS
- 🌏 Coordinate Storage - Captures latitude/longitude with address
- 📝 Manual Entry Support - Allow users to type addresses manually
- ⚠️ Field Validation - Built-in error display for each field
- 🎨 Themeable - Fully customizable to match your app design
Local Database Dropdown Widgets - Offline country/state/city selection:
- ✅ CountryDropdownField - 250+ countries with flags and phone codes
- ✅ StateDropdownField - States/provinces filtered by country
- ✅ CityDropdownField - Cities filtered by state with coordinates
- 🔍 Searchable - Instant filtering as you type
- 🗂️ Cascading - Country → State → City selection flow
- 📱 Offline - Works without internet (bundled database)
Individual Field Widgets - Use address fields separately:
- ✅ StreetAddressField - Street address field
- ✅ CityField - City field
- ✅ StateField - State field
- ✅ CountryField - Country field
- ✅ PostalCodeField - Postal code field
Features
Complete Address Widget
- ✅ Google Places Integration - Search addresses with real-time suggestions (optional)
- ✅ GPS Location Detection - One-click current location with reverse geocoding (works WITHOUT Google API!)
- ✅ Multi-Field Support - Street, City, State, Country, Postal Code
- ✅ Coordinate Capture - Automatically stores latitude and longitude
- ✅ Country Restrictions - Limit searches to specific countries (India & US by default)
- ✅ Smart Field Management - Auto-lock fields from Google Places or GPS data
- ✅ Manual Override - Clear to allow manual address entry
- ✅ Field Validation - Display errors per field with clear visual feedback
- ✅ Source Tracking - Know if address is from Google Places, GPS, or manual entry
- ✅ Locked Address Support - Perfect for business addresses that shouldn't change
- ✅ Clean Material Design - Beautiful, modern UI out of the box
Local Database Dropdown Widgets
- ✅ Offline Operation - No internet required, uses bundled database
- ✅ 250+ Countries - Complete country list with flags and phone codes
- ✅ States/Provinces - Comprehensive state data by country
- ✅ Cities - Thousands of cities worldwide with coordinates
- ✅ Searchable - Type to filter options instantly
- ✅ Cascading Selection - Country → State → City flow
- ✅ Rich Data - Includes capitals, coordinates, currency, and more
Individual Field Widgets
- ✅ Modular Design - Use only the fields you need
- ✅ Custom Layouts - Arrange fields however you want
- ✅ Full Feature Parity - Same styling and validation as complete widget
- ✅ Controller Support - Full TextEditingController integration
- ✅ Easy Integration - Drop-in replacements for standard TextFields
Installation
Add this to your package's pubspec.yaml file:
dependencies:
location_picker_plus: ^3.1.0
🗝️ Google Places API Setup
- Get API Key: Visit Google Cloud Console
- Enable APIs: Enable "Places API" in your project
- Configure Billing: Ensure billing is enabled for your project
- Restrict Key (recommended): Restrict your API key to specific platforms
Usage
Basic Example
import 'package:flutter/material.dart';
import 'package:location_picker_plus/location_picker_plus.dart';
class MyAddressForm extends StatefulWidget {
@override
_MyAddressFormState createState() => _MyAddressFormState();
}
class _MyAddressFormState extends State<MyAddressForm> {
AddressFormData? _currentAddress;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(16),
child: CustomStreetAddressField(
googleApiKey: 'YOUR_GOOGLE_PLACES_API_KEY',
showAllFields: true,
onAddressChanged: (addressData) {
setState(() {
_currentAddress = addressData;
});
// Access address data
print('Street: ${addressData.street}');
print('City: ${addressData.city}');
print('State: ${addressData.state}');
print('Country: ${addressData.country}');
print('Postal Code: ${addressData.postalCode}');
print('Coordinates: ${addressData.latitude}, ${addressData.longitude}');
print('From Google Places: ${addressData.isFromGooglePlaces}');
},
onPlaceSelected: (place) {
// Called when a Google Place is selected
print('Selected place: ${place?.address}');
},
),
),
);
}
}
With Field Validation
class _MyFormState extends State<MyForm> {
final Map<String, String> _fieldErrors = {};
@override
Widget build(BuildContext context) {
return CustomStreetAddressField(
googleApiKey: 'YOUR_API_KEY',
showAllFields: true,
fieldErrors: _fieldErrors,
onFieldErrorCleared: (fieldKey) {
setState(() {
_fieldErrors.remove(fieldKey);
});
},
onAddressChanged: (addressData) {
// Validate
if (addressData.street == null || addressData.street!.isEmpty) {
setState(() {
_fieldErrors['address'] = 'Please enter a valid address';
});
}
},
);
}
}
Locked Address (for Business Locations)
CustomStreetAddressField(
googleApiKey: 'YOUR_API_KEY',
isLocked: true, // Prevents editing
initialStreet: '1600 Amphitheatre Parkway',
initialCity: 'Mountain View',
initialState: 'CA',
initialCountry: 'US',
initialPostalCode: '94043',
initialLatitude: 37.4220,
initialLongitude: -122.0841,
showAllFields: true,
)
Street Address Only (No Additional Fields)
CustomStreetAddressField(
googleApiKey: 'YOUR_API_KEY',
showAllFields: false, // Only show street address field
hintText: 'Enter delivery address...',
onAddressChanged: (addressData) {
print('Street only: ${addressData.street}');
},
)
GPS Location Detection (Works WITHOUT Google Places API!)
CustomStreetAddressField(
enableLocationPicker: true, // Enable GPS button
showAllFields: true,
onAddressChanged: (addressData) {
print('Address: ${addressData.street}');
print('Coordinates: ${addressData.latitude}, ${addressData.longitude}');
},
onLocationSelected: (position) {
print('GPS detected: ${position?.latitude}, ${position?.longitude}');
},
)
Note: GPS location detection uses device GPS + reverse geocoding (via geocoding package). It does NOT require a Google Places API key!
Using Individual Field Widgets
Perfect for custom layouts or when you only need specific fields:
class MyCustomForm extends StatelessWidget {
final TextEditingController streetController = TextEditingController();
final TextEditingController cityController = TextEditingController();
@override
Widget build(BuildContext context) {
return Column(
children: [
// Just the street address field
StreetAddressField(
controller: streetController,
onChanged: (value) {
print('Street: $value');
},
),
SizedBox(height: 16),
// City and State side by side
Row(
children: [
Expanded(
child: CityField(
controller: cityController,
errorText: cityController.text.isEmpty ? 'Required' : null,
),
),
SizedBox(width: 10),
Expanded(
child: StateField(
onChanged: (value) => print('State: $value'),
),
),
],
),
// Just postal code
PostalCodeField(
onChanged: (value) => print('Postal: $value'),
),
],
);
}
}
Available Individual Widgets:
StreetAddressField- Street address with 2 lines supportCityField- City inputStateField- State/Province inputCountryField- Country inputPostalCodeField- Postal/ZIP code (numeric keyboard)AddressTextField- Generic base widget for custom address fields
Using Local Database Dropdown Widgets
Perfect for offline country/state/city selection with cascading filters:
import 'package:flutter/material.dart';
import 'package:location_picker_plus/location_picker_plus.dart';
class LocationDropdownForm extends StatefulWidget {
@override
_LocationDropdownFormState createState() => _LocationDropdownFormState();
}
class _LocationDropdownFormState extends State<LocationDropdownForm> {
CountryModel? _selectedCountry;
StateModel? _selectedState;
CityModel? _selectedCity;
@override
Widget build(BuildContext context) {
return Column(
children: [
// Country Dropdown with flags
CountryDropdownField(
initialValue: _selectedCountry,
onChanged: (country) {
setState(() {
_selectedCountry = country;
_selectedState = null; // Reset state when country changes
_selectedCity = null; // Reset city when country changes
});
// Access country data
print('Country: ${country?.name}');
print('Code: ${country?.sortName}');
print('Phone Code: ${country?.phoneCode}');
print('Flag: ${country?.flagEmoji}');
},
),
SizedBox(height: 16),
// State Dropdown (filtered by selected country)
StateDropdownField(
initialValue: _selectedState,
countryId: _selectedCountry?.id, // Filter by country
onChanged: (state) {
setState(() {
_selectedState = state;
_selectedCity = null; // Reset city when state changes
});
print('State: ${state?.name}');
print('State Code: ${state?.stateCode}');
},
),
SizedBox(height: 16),
// City Dropdown (filtered by selected state)
CityDropdownField(
initialValue: _selectedCity,
stateId: _selectedState?.id, // Filter by state
onChanged: (city) {
setState(() {
_selectedCity = city;
});
print('City: ${city?.name}');
print('Coordinates: ${city?.latitude}, ${city?.longitude}');
print('Is Capital: ${city?.isCapital}');
},
),
],
);
}
}
Dropdown Features:
- Searchable Dialogs - Type to filter options instantly
- Country Flags - Visual flag emojis for all countries
- Cascading Selection - Selecting country filters states, selecting state filters cities
- Rich Data Access - Phone codes, coordinates, capitals, currency, and more
- Offline Database - No internet required, uses bundled JSON data
- Performance Optimized - Efficient search with thousands of locations
Available Dropdown Widgets:
CountryDropdownField- Select from 250+ countriesStateDropdownField- Select states/provinces (filtered by country)CityDropdownField- Select cities (filtered by state)
Accessing Location Service Directly:
import 'package:location_picker_plus/location_picker_plus.dart';
// Load all countries
List<CountryModel> countries = await LocationService.instance.loadCountries();
// Get states by country ID
List<StateModel> states = await LocationService.instance.getStatesByCountryId(101); // India
// Get cities by state ID
List<CityModel> cities = await LocationService.instance.getCitiesByStateId(4008); // California
// Access model data
CountryModel country = countries.first;
print('${country.name} ${country.flagEmoji}'); // India 🇮🇳
print('Phone: +${country.phoneCode}'); // Phone: +91
print('Capital: ${country.capital}'); // Capital: New Delhi
print('Currency: ${country.currency}'); // Currency: INR
StateModel state = states.first;
print('${state.name} (${state.stateCode})'); // Maharashtra (MH)
CityModel city = cities.first;
print('${city.name}'); // Mumbai
print('Coords: ${city.latitude}, ${city.longitude}');
print('Capital: ${city.isCapital}'); // true/false
Widget Parameters
CustomStreetAddressField
| Parameter | Type | Description | Default |
|---|---|---|---|
googleApiKey |
String? |
Google Places API key (optional - not needed for GPS) | null |
enableLocationPicker |
bool |
Show GPS location button (works without Google API) | false |
initialStreet |
String? |
Initial street address | null |
initialCity |
String? |
Initial city | null |
initialState |
String? |
Initial state | null |
initialCountry |
String? |
Initial country | null |
initialPostalCode |
String? |
Initial postal code | null |
initialLatitude |
double? |
Initial latitude | null |
initialLongitude |
double? |
Initial longitude | null |
showAllFields |
bool |
Show city, state, country, postal code fields | true |
isLocked |
bool |
Lock all fields (for business addresses) | false |
hintText |
String? |
Hint text for street address field | 'Search for addresses...' |
fieldErrors |
Map<String, String>? |
Map of field keys to error messages | null |
onAddressChanged |
ValueChanged<AddressFormData>? |
Called when any address field changes | null |
onPlaceSelected |
ValueChanged<LocationResult?>? |
Called when Google Place is selected | null |
onLocationSelected |
ValueChanged<Position?>? |
Called when GPS location is detected | null |
onFieldErrorCleared |
ValueChanged<String>? |
Called when user starts editing a field with an error | null |
AddressFormData
class AddressFormData {
final String? street;
final String? city;
final String? state;
final String? country;
final String? postalCode;
final double? latitude;
final double? longitude;
final bool isFromGooglePlaces;
}
Field Error Keys
Use these keys in the fieldErrors map:
'address'- Street address field'city'- City field'state'- State field'country'- Country field'postalCode'- Postal code field
Country Restrictions
By default, the widget restricts searches to India (IN) and United States (US). This is configured in the widget and can be modified by editing the source code if you need different countries.
Customization
The widget uses Material Design theming and respects your app's Theme:
MaterialApp(
theme: ThemeData(
primaryColor: Colors.blue,
textTheme: TextTheme(...),
),
...
)
Requirements
- Flutter SDK:
>=3.3.0 - Dart SDK:
^3.9.2 - Dependencies:
geolocator: ^14.0.2http: ^1.2.0
Platform Setup
Android
Add to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<!-- For GPS location detection -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
iOS
Add to ios/Runner/Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<!-- For GPS location detection -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs your location to auto-fill your address</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs your location to auto-fill your address</string>
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.
Issues
If you encounter any problems, please file an issue on the GitHub repository.
Changelog
3.1.0
- ✨ NEW: GPS Location Detection - One-click location with reverse geocoding (works WITHOUT Google API!)
- ✨ NEW:
CountryDropdownField- Select from 250+ countries with flags - ✨ NEW:
StateDropdownField- Select states/provinces by country - ✨ NEW:
CityDropdownField- Select cities by state - ✨ NEW: Individual text field widgets (
StreetAddressField,CityField,StateField,CountryField,PostalCodeField) - 🔍 Searchable dropdowns with instant filtering
- 🗂️ Cascading selection (Country → State → City)
- 📱 Works completely offline with bundled database
- 📍 Includes coordinates for cities
- ⭐ Shows capital cities
- 🔐 Automatic GPS permission handling for Android & iOS
- 🟢 Visual indicators for GPS data (green badge)
- 🔒 Smart field locking for GPS data
- 📦 Exported
CountryModel,StateModel,CityModel, andLocationService - 🎨 Enhanced UI with color-coded field locking (blue for Google Places, green for GPS)
- 🐛 Fixed deprecated
withOpacitycalls (now usingwithValues) - 📚 Comprehensive documentation and examples
3.0.0
- BREAKING: Removed all previous widgets
- ✨ NEW:
CustomStreetAddressField- Complete address entry widget - 🔍 Integrated Google Places API for address autocomplete (optional)
- 📍 Complete address field support (street, city, state, country, postal code)
- 🌍 Coordinate capture (latitude, longitude)
- 🔒 Smart field locking from Google Places data
- ⚠️ Field validation and error display
- 🏢 Support for locked business addresses
- 🎨 Simplified API and improved developer experience
Author
Brewnbeer Team
Support
⭐ Star this repository if you find it helpful!
Libraries
- location_picker_plus
- location_picker_plus_method_channel
- location_picker_plus_platform_interface
- location_picker_plus_web
- models/city_model
- models/country_model
- models/location_model
- models/location_result
- models/state_model
- services/google_places_service
- services/location_detector_service
- services/location_service
- themes/enhanced_location_theme
- themes/location_picker_plus_theme
- utils/adaptive_debouncer
- widgets/address_dropdown_fields
- widgets/address_text_field
- widgets/custom_street_address_field