Location Picker Plus

Advanced Flutter location picker with Google Places API integration, GPS detection, manual entry, and comprehensive theming. The ultimate solution for location selection with fallback options.

πŸš€ v3.0.0 - Major Release with Google Places Integration

🌟 Three Powerful Widgets for Every Use Case

πŸ“ LocationPickerWidget

Traditional dropdown selection from predefined country/state/city data with beautiful customization.

🌍 LocationDetectorWidget

Live GPS location detection and address geocoding with real-time coordinate conversion.

πŸ”₯ LocationPickerPlusUnifiedWidget (NEW v3.0.0)

All-in-one widget combining Google Places Autocomplete, GPS detection, and manual entry with intelligent tab-based interface.

Features

Traditional Location Picker

  • βœ“ Two Input Modes - Dropdown with search OR Real-time autocomplete
  • βœ“ Instant Suggestions - See suggestions as you type (no Enter key needed)
  • βœ“ Debounced Performance - Optimized for smooth typing experience
  • βœ“ Customizable Themes - Multiple pre-built themes and full customization support
  • βœ“ Smart Search - Built-in search with relevance sorting
  • βœ“ Flag Emojis & Phone Codes - Display country flags and phone codes
  • βœ“ Flexible Layouts - Horizontal/vertical layouts with responsive design
  • βœ“ Asset-based Data - Load location data from JSON assets
  • βœ“ Enhanced Models - Rich data models with additional properties
  • βœ“ Animation Support - Smooth transitions and animations
  • βœ“ Accessibility - Screen reader support and keyboard navigation

πŸ†• Live Location Detection

  • 🌍 GPS Location Detection - Get current location with one tap
  • πŸ” Address Geocoding - Search any address and get coordinates
  • 🏠 Reverse Geocoding - Convert coordinates to readable addresses
  • πŸ“ Forward Geocoding - Convert addresses to coordinates
  • πŸ” Auto Permission Handling - Handles all location permissions automatically
  • ⚑ High Accuracy GPS - Configurable accuracy levels
  • 🌐 Multi-locale Support - Address detection in multiple languages
  • πŸ“± Cross-platform - Full Android and iOS support
  • πŸŽ›οΈ Flexible Modes - GPS only, search only, or both
  • 🎨 Full UI Customization - Match your app's design

πŸš€ NEW v3.0.0 - Google Places Integration

  • πŸ” Google Places Autocomplete - Real-time address search with global coverage
  • 🏒 Business & POI Support - Find restaurants, hotels, landmarks, and more
  • πŸ“‹ Tabbed Interface - Clean navigation between Google Places, GPS, and manual entry
  • πŸ”’ Smart Field Locking - Auto-lock fields when address selected from Google Places
  • 🌏 Country Restrictions - Limit search results to specific countries
  • πŸ“ Address Source Tracking - Know if address came from Google Places, GPS, or manual entry
  • 🎨 Enhanced Theming - Comprehensive theming system with Material and Cupertino themes
  • πŸ“± Mobile Optimized - Touch-friendly interface with responsive design

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  location_picker_plus: ^3.0.0

πŸ—οΈ Google Places API Setup (NEW v3.0.0)

  1. Get API Key: Visit Google Cloud Console
  2. Enable APIs: Enable "Places API" in your project
  3. Configure Billing: Ensure billing is enabled for your project
  4. Restrict Key (recommended): Restrict your API key to specific platforms
LocationPickerPlusUnifiedWidget(
  googlePlacesApiKey: 'YOUR_API_KEY_HERE',
  // ... other parameters
)

πŸ“± Platform Setup (for GPS Location Features)

Android

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

iOS

Add to ios/Runner/Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to detect your current location.</string>

Usage

πŸ†• Live Location Detection

Quick Start

import 'package:location_picker_plus/location_picker_plus.dart';

LocationDetectorWidget(
  mode: LocationDetectorMode.both, // GPS + Address search
  showCoordinates: true,
  onLocationChanged: (location) {
    print('Lat: ${location?.latitude}, Lng: ${location?.longitude}');
    print('Address: ${location?.fullAddress}');
    print('City: ${location?.locality}');
    print('State: ${location?.administrativeArea}');
    print('Country: ${location?.country}');
  },
)

GPS Detection Only

LocationDetectorWidget(
  mode: LocationDetectorMode.currentLocation,
  onCoordinatesChanged: (lat, lng) {
    print('Coordinates: $lat, $lng');
  },
)

Address Search Only

LocationDetectorWidget(
  mode: LocationDetectorMode.addressSearch,
  addressSearchHint: 'Enter address, city, or landmark...',
  onLocationChanged: (location) {
    // Use detected location data
  },
)

Advanced Usage with Service

// Direct service usage for custom UI
LocationDetectorService service = LocationDetectorService.instance;

// Get current location
LocationModel? location = await service.getCurrentLocationWithAddress();
print('Current city: ${location?.locality}');

// Search address
LocationModel? searched = await service.getCoordinatesFromAddress('New York');
print('NYC coordinates: ${searched?.latitude}, ${searched?.longitude}');

πŸ”₯ Unified Location Picker with Google Places Integration (NEW v3.0.0)

The ultimate all-in-one widget combining Google Places Autocomplete, GPS detection, and manual entry with intelligent tab-based interface and smart field management.

🎯 Smart Field Behavior

  • Google Places Selection β†’ Fields automatically lock for accuracy
  • Manual Entry β†’ All fields remain fully editable
  • GPS Location β†’ All fields remain editable
  • Country Restrictions β†’ Limit countries in dropdown

Quick Start - All Modes

LocationPickerPlusUnifiedWidget(
  mode: LocationPickerMode.all, // Show all 3 tabs
  googlePlacesApiKey: 'your-api-key',
  allowedCountries: ['India', 'United States'], // Optional restriction
  lockFieldsForGooglePlaces: true, // Default: true
  onLocationSelected: (location) {
    print('Selected: ${location?.address}');
    print('Source: ${location?.addressSource}'); // googlePlaces, manualEntry, or gpsLocation

    if (location?.addressSource == AddressSource.googlePlaces) {
      print('Address from Google Places - fields are locked');
    } else {
      print('Manual/GPS address - fields are editable');
    }
  },
)

Google Places Only Mode

LocationPickerPlusUnifiedWidget(
  mode: LocationPickerMode.googlePlaces,
  googlePlacesApiKey: 'your-api-key',
  country: 'US', // Restrict to US addresses
  placesTypes: ['establishment', 'geocode'],
  onLocationSelected: (location) {
    // Google Places result with locked fields in manual entry
  },
)

Manual Entry with Country Restrictions

LocationPickerPlusUnifiedWidget(
  mode: LocationPickerMode.manualEntry,
  allowedCountries: ['IN', 'US', 'CA'], // ISO codes or full names
  lockFieldsForGooglePlaces: false, // Allow editing Google Places results
  onLocationSelected: (location) {
    // Manual entry result - always editable
  },
)

GPS + Manual Entry (No Google Places)

LocationPickerPlusUnifiedWidget(
  mode: LocationPickerMode.all,
  googlePlacesApiKey: null, // Disable Google Places tab
  autoDetectOnInit: true, // Auto-detect GPS on load
  allowedCountries: ['India', 'United States'],
  onLocationSelected: (location) {
    // GPS or manual entry result
  },
)

Address Source Tracking

onLocationSelected: (LocationResult? location) {
  switch (location?.addressSource) {
    case AddressSource.googlePlaces:
      print('🌐 From Google Places - High accuracy, fields locked');
      break;
    case AddressSource.manualEntry:
      print('✏️ Manual entry - User can edit all fields');
      break;
    case AddressSource.gpsLocation:
      print('πŸ“ From GPS - Detected location, fields editable');
      break;
  }
}

πŸ“ Traditional Location Picker

Basic Usage

LocationPickerWidget(
  onCountryChanged: (country) {
    print('Selected country: ${country?.name}');
  },
  onStateChanged: (state) {
    print('Selected state: ${state?.name}');
  },
  onCityChanged: (city) {
    print('Selected city: ${city?.name}');
    // Access latitude/longitude from city data
    if (city?.latitude != null) {
      print('City coordinates: ${city?.latitude}, ${city?.longitude}');
    }
  },
)

Autocomplete Mode (Real-time suggestions as you type)

LocationPickerWidget(
  useAutocomplete: true, // Enable autocomplete mode
  countryHint: 'Type country name...',
  stateHint: 'Type state name...',
  cityHint: 'Type city name...',
  onCountryChanged: (country) {
    // Handle country selection
  },
)
LocationPickerWidget(
  useAutocomplete: false, // Traditional dropdown mode
  theme: LocationPickerTheme.materialTheme().copyWith(
    showFlags: true,
    showPhoneCodes: true,
    borderRadius: BorderRadius.circular(12),
  ),
  countryLabel: 'Country',
  stateLabel: 'State/Province',
  cityLabel: 'City',
  onCountryChanged: (country) {
    // Handle country selection
  },
)

Country Only Picker

LocationPickerWidget(
  useAutocomplete: true, // Or false for dropdown
  showState: false,
  showCity: false,
  countryHint: 'Type country name...',
  onCountryChanged: (country) {
    // Handle country selection
  },
)

Horizontal Layout

Row(
  children: [
    Expanded(
      child: LocationPickerWidget(
        showState: false,
        showCity: false,
        countryLabel: 'Country',
      ),
    ),
    SizedBox(width: 16),
    Expanded(
      child: LocationPickerWidget(
        showCountry: false,
        showCity: false,
        stateLabel: 'State',
      ),
    ),
  ],
)

πŸ”„ Combining Both Widgets

You can use both widgets together for maximum flexibility:

Column(
  children: [
    // Live location detection
    LocationDetectorWidget(
      mode: LocationDetectorMode.currentLocation,
      onLocationChanged: (location) {
        // Auto-fill traditional picker based on detected location
      },
    ),

    SizedBox(height: 20),

    // Traditional picker for manual selection
    LocationPickerWidget(
      useAutocomplete: true,
      onCountryChanged: (country) {
        // Handle manual selection
      },
    ),
  ],
)

Customization

Available Themes

// Default theme
LocationPickerTheme.defaultTheme()

// Material Design theme
LocationPickerTheme.materialTheme()

// Cupertino theme
LocationPickerTheme.cupertinoTheme()

// Custom theme
LocationPickerTheme(
  inputDecoration: InputDecoration(
    border: OutlineInputBorder(),
    contentPadding: EdgeInsets.all(16),
  ),
  dropdownBackgroundColor: Colors.white,
  itemHighlightColor: Colors.blue.withOpacity(0.1),
  borderRadius: BorderRadius.circular(8),
  elevation: 4,
  showFlags: true,
  showPhoneCodes: false,
  maxHeight: 250,
  animationDuration: Duration(milliseconds: 200),
)

Input Modes

Autocomplete Mode (useAutocomplete: true)

  • Real-time suggestions appear as you type
  • No Enter key required - suggestions show instantly
  • Debounced for performance - optimized for smooth typing
  • Smart relevance sorting - exact matches first, then starts-with, then contains
  • Auto-clear functionality - click X to clear selection
  • Traditional dropdown with search functionality inside
  • Click to open dropdown with search box
  • Keyboard navigation support
  • More familiar UX for users expecting dropdowns

Widget Parameters

Parameter Type Description
useAutocomplete bool Enable autocomplete mode (true) or dropdown mode (false). Default: true
showCountry bool Show country picker. Default: true
showState bool Show state picker. Default: true
showCity bool Show city picker. Default: true
countryHint String? Hint text for country field
stateHint String? Hint text for state field
cityHint String? Hint text for city field
theme LocationPickerTheme? Custom theme for styling
onCountryChanged Function(CountryModel?)? Callback when country is selected
onStateChanged Function(StateModel?)? Callback when state is selected
onCityChanged Function(CityModel?)? Callback when city is selected

Models

CountryModel

CountryModel(
  id: '1',
  sortName: 'US',
  name: 'United States',
  phoneCode: '1',
  flagEmoji: 'πŸ‡ΊπŸ‡Έ',
  capital: 'Washington, D.C.',
  currency: 'USD',
)

StateModel

StateModel(
  id: '1',
  name: 'California',
  countryId: '1',
  stateCode: 'CA',
  type: 'State',
)

CityModel

CityModel(
  id: '1',
  name: 'Los Angeles',
  stateId: '1',
  latitude: '34.0522',
  longitude: '-118.2437',
  isCapital: false,
)

πŸ†• LocationModel (Live Location)

LocationModel(
  latitude: 34.0522,
  longitude: -118.2437,
  address: '123 Main St, Los Angeles, CA 90210, USA',
  street: '123 Main St',
  locality: 'Los Angeles',        // City
  administrativeArea: 'CA',       // State
  country: 'United States',
  postalCode: '90210',
  countryCode: 'US',
)

πŸ”₯ LocationResult (NEW v2.1.0 - Unified Widget)

LocationResult(
  latitude: 34.0522,
  longitude: -118.2437,
  address: '123 Main St, Los Angeles, CA 90210, USA',
  city: 'Los Angeles',
  state: 'California',
  country: 'United States',
  postalCode: '90210',
  addressSource: AddressSource.googlePlaces, // googlePlaces, manualEntry, or gpsLocation
)

AddressSource Enum

enum AddressSource {
  googlePlaces,  // Address selected from Google Places dropdown
  manualEntry,   // User manually entered address
  gpsLocation,   // Address detected from GPS coordinates
}

πŸ”₯ LocationPickerPlusUnifiedWidget Parameters (NEW v3.0.0)

Parameter Type Description
mode LocationPickerMode Widget mode: googlePlaces, currentLocation, manualEntry, or all. Default: all
googlePlacesApiKey String? Google Places API key for address search
allowedCountries List<String>? Restrict countries in dropdown (names or ISO codes like ['IN', 'US'])
lockFieldsForGooglePlaces bool Lock manual entry fields when Google Places address is selected. Default: true
onLocationSelected Function(LocationResult?)? Called when location is selected from any source
country String? Restrict Google Places to specific country (ISO code)
placesTypes List<String> Google Places types filter. Default: []
placesHintText String Google Places search hint. Default: 'Search for a place...'
useDropdownsForCountryState bool Use dropdowns for country/state in manual entry. Default: true
autoDetectOnInit bool Auto-detect GPS location on widget load. Default: false
showCoordinates bool Show coordinates in location display. Default: true
theme LocationPickerTheme? Custom theme for styling

πŸ“Š LocationDetectorWidget Parameters

Parameter Type Description
mode LocationDetectorMode Detection mode: currentLocation, addressSearch, or both
showCoordinates bool Show latitude/longitude in results. Default: true
showFullAddress bool Show complete address breakdown. Default: true
autoDetectOnInit bool Auto-detect location on widget load. Default: false
accuracy LocationAccuracy GPS accuracy level. Default: LocationAccuracy.high
timeLimit Duration? Max time to wait for location. Default: null
currentLocationLabel String Label for GPS button. Default: 'Current Location'
addressSearchLabel String Label for search field. Default: 'Search Address'
addressSearchHint String? Placeholder for search field
theme LocationPickerTheme? Custom theme for styling
onLocationChanged Function(LocationModel?)? Called when location is detected/searched
onAddressChanged Function(String)? Called when address changes
onCoordinatesChanged Function(double, double)? Called when coordinates change

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License.