map_location_picker 2.0.0+1 copy "map_location_picker: ^2.0.0+1" to clipboard
map_location_picker: ^2.0.0+1 copied to clipboard

Google Map location picker for flutter Based on google_maps_flutter.

map_location_picker: #

Pub Version GitHub Repo stars GitHub Repo forks GitHub Repo issues GitHub Repo contributors

Modern Location Picker for Flutter with Enhanced UI & Customization #

Version 2.0 introduces a complete overhaul with:

Check migration guide from 1.x to 2.x map_location_picker/MIGRATION_GUIDE

  • 🍎 New Cupertino-style UI components
  • πŸŒ— Built-in dark/light theme support
  • 🧩 Modular configuration architecture
  • πŸš€ Performance optimizations
  • πŸ—ΊοΈ Advanced map customization options
  • 🧭 Improved navigation and UI flow

Check out the more screenshots here

Map Preview Address Display Picker Options

πŸš€ Getting Started #

Installation #

Add to your pubspec.yaml:

dependencies:
  map_location_picker: ^2.0.0

Setup Guide #

For more details, see Getting started with Google Maps Platform.

Android #

  1. Set the minSdkVersion in android/app/build.gradle:
android {
    defaultConfig {
        minSdkVersion 20
    }
}

This means that app will only be available for users that run Android SDK 20 or higher.

  1. Specify your API key in the application manifest android/app/src/main/AndroidManifest.xml:

<manifest ...
<application ...
<meta-data android:name="com.google.android.geo.API_KEY"
           android:value="YOUR KEY HERE"/>

Hybrid Composition

To use Hybrid Composition to render the GoogleMap widget on Android, set AndroidGoogleMapsFlutter.useAndroidViewSurface to true.

if (defaultTargetPlatform == TargetPlatform.android) {
AndroidGoogleMapsFlutter.useAndroidViewSurface = true;
}

iOS #

To set up, specify your API key in the application delegate ios/Runner/AppDelegate.m:

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#import "GoogleMaps/GoogleMaps.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GMSServices provideAPIKey:@"YOUR KEY HERE"];
  [GeneratedPluginRegistrant registerWithRegistry:self];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

Or in your swift code, specify your API key in the application delegate ios/Runner/AppDelegate.swift:

import UIKit
import Flutter
import GoogleMaps
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GMSServices.provideAPIKey("YOUR KEY HERE")
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
Screenshot 2025-01-06 at 21 04 43

You must then send the following in the headers:

  Map<String, String> headers = {};
  if (Platform.isIOS || Platform.isMacOS) {
    headers['X-Ios-Bundle-Identifier'] = 'Your Bundle Identifier';
  }
  if (Platform.isAndroid) {
    headers['X-Android-Package'] = 'Your Bundle Identifier';
    headers['X-Android-Cert'] = 'Your Sha-1';
  }
   MapLocationPicker(
      geoCodingApiHeaders: headers,
      ...
   )

Web View #

Modify web/index.html

Get an API Key for Google Maps JavaScript API. Get started here.

Modify the <head> tag of your web/index.html to load the Google Maps JavaScript API, like so:

<head>
  <!-- // Other stuff -->

  <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
</head>

Note #

The following permissions are not required to use Google Maps Android API v2, but are recommended.

android.permission.ACCESS_COARSE_LOCATION Allows the API to use WiFi or mobile cell data (or both) to determine the device's location. The API returns the location with an accuracy approximately equivalent to a city block.

android.permission.ACCESS_FINE_LOCATION Allows the API to determine as precise a location as possible from the available location providers, including the Global Positioning System (GPS) as well as WiFi and mobile cell data.


You must also explicitly declare that your app uses the android.hardware.location.network or android.hardware.location.gps hardware features if your app targets Android 5.0 (API level 21) or higher and uses the ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission in order to receive location updates from the network or a GPS, respectively.


<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>

The following permissions are defined in the package manifest, and are automatically merged into your app's manifest at build time. You don't need to add them explicitly to your manifest:

android.permission.INTERNET Used by the API to download map tiles from Google Maps servers.

android.permission.ACCESS_NETWORK_STATE Allows the API to check the connection status in order to determine whether data can be downloaded.

Restricting Autocomplete Search to Region #

The Results returned can be restricted to certain countries by passing an array of country codes into the components parameter of MapLocationPicker. Countries must be two character, ISO 3166-1 Alpha-2 compatible. You can find code information at Wikipedia: List of ISO 3166 country codes or the ISO Online Browsing Platform.

Basic Usage #

import 'package:map_location_picker/map_location_picker.dart';

MapLocationPicker(
  config: MapPickerConfig(
    apiKey: "YOUR_API_KEY",
    initialPosition: LatLng(37.7749, -122.4194),
  ),
  searchConfig: PlacesAutocompleteConfig(
    apiKey: "YOUR_API_KEY",
    searchHintText: "Search locations...",
  ),
);

πŸ†• What's New in 2.0.0 #

1. Cupertino-Style UI #

The entire UI has been redesigned with Cupertino components for a native iOS feel:

PlacesAutocomplete(
  config: PlacesAutocompleteConfig(
    // Uses CupertinoTypeAheadField internally
    searchHintText: "Search locations...",
  ),
);

2. Theme Support #

Built-in support for light/dark themes with automatic switching:

final _themeMode = ValueNotifier<ThemeMode>(ThemeMode.light);

MapLocationPicker(
  config: MapPickerConfig(
    // Automatically adapts to current theme
  ),
);

3. Enhanced Configuration #

More granular control with expanded configuration objects:

MapPickerConfig(
  mapTypeButton: CustomMapTypeButton(), // Fully customizable buttons
  locationButton: CustomLocationButton(),
  bottomCardBuilder: (ctx, result, address, isLoading, onNext) {
    return CustomBottomCard(address: address);
  },
);

4. Improved Map Previews #

New static map previews for selected locations:

Image.network(
  googleStaticMapWithMarker(
    _pickedLocation!.latitude,
    _pickedLocation!.longitude,
    18,
    apiKey: YOUR_API_KEY,
  ),
);

5. Advanced Marker Support #

Custom markers with asset-based icons:

void _createMarkerIcon() async {
  _customMarkerIcon = await BitmapDescriptor.asset(
    const ImageConfiguration(size: Size(48, 48)),
    'assets/marker.webp',
  );
}

MapPickerConfig(
  mainMarkerIcon: _customMarkerIcon,
);

🌟 Key Features #

PlacesAutocomplete(
  config: PlacesAutocompleteConfig(
    searchHintText: "Search locations...",
    itemBuilder: (context, prediction) => CupertinoListTile(
      title: Text(prediction.description ?? ""),
      subtitle: Text(prediction.secondaryText ?? ""),
    ),
  ),
);

Theme-Aware Components #

// Automatically adapts to light/dark themes
MapLocationPicker(
  config: MapPickerConfig(
    floatingControlsColor: Theme.of(context).colorScheme.primary,
    floatingControlsIconColor: Theme.of(context).colorScheme.onPrimary,
  ),
);

Customizable Bottom Sheets #

MapPickerConfig(
  bottomCardBuilder: (context, result, address, isLoading, onNext) {
    return CupertinoActionSheet(
      title: const Text("Selected Location"),
      actions: [
        CupertinoActionSheetAction(
          onPressed: onNext,
          child: Text(address),
        ),
      ],
    );
  },
);

Advanced Marker Configuration #

MapPickerConfig(
  additionalMarkers: const {
    "landmark1": LatLng(37.422, -122.084),
    "landmark2": LatLng(37.426, -122.083),
  },
  customMarkerIcons: {
    "landmark1": BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueYellow),
    "landmark2": BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue),
  },
  customInfoWindows: const {
    "landmark1": InfoWindow(title: "Golden Gate Bridge"),
  },
);

πŸ›  Setup Guide #

API Keys Setup #

  1. Get an API key at Google Cloud Console
  2. Enable required APIs:
    • Maps SDK for Android/iOS
    • Places API
    • Geocoding API
    • Maps JavaScript API (for web)

Android Setup #

Add to AndroidManifest.xml:

<meta-data
  android:name="com.google.android.geo.API_KEY"
  android:value="YOUR_ANDROID_API_KEY"/>

iOS Setup #

Add to AppDelegate.swift:

GMSServices.provideAPIKey("YOUR_IOS_API_KEY")

Web Setup #

Add to web/index.html:

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_WEB_API_KEY"></script>

πŸ’» Example App #

import 'package:example/key.dart';
import 'package:flutter/material.dart';
import 'package:map_location_picker/map_location_picker.dart';

void main() => runApp(const MyApp());

final _themeMode = ValueNotifier<ThemeMode>(ThemeMode.light);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<ThemeMode>(
      valueListenable: _themeMode,
      builder: (context, themeMode, child) {
        return MaterialApp(
          theme: ThemeData.light(),
          darkTheme: ThemeData.dark(),
          themeMode: themeMode,
          home: const LocationPickerScreen(),
        );
      },
    );
  }
}

class LocationPickerScreen extends StatefulWidget {
  const LocationPickerScreen({super.key});

  @override
  State<LocationPickerScreen> createState() => _LocationPickerScreenState();
}

class _LocationPickerScreenState extends State<LocationPickerScreen> {
  LatLng? _pickedLocation;
  String _formattedAddress = "No location selected";
  BitmapDescriptor? _customMarkerIcon;

  @override
  void initState() {
    super.initState();
    _createMarkerIcon();
  }

  void _createMarkerIcon() async {
    _customMarkerIcon = await BitmapDescriptor.asset(
      const ImageConfiguration(size: Size(48, 48)),
      'assets/marker.webp',
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Location Picker')),
      body: SingleChildScrollView(
        child: Column(
          children: [
            // Map preview
            Container(
              height: 200,
              child: _pickedLocation == null
                ? Center(child: Text("Select a location"))
                : Image.network(
                    googleStaticMapWithMarker(
                      _pickedLocation!.latitude,
                      _pickedLocation!.longitude,
                      16,
                      apiKey: YOUR_API_KEY,
                    ),
                    fit: BoxFit.cover,
                  ),
            ),

            // Address display
            ListTile(
              leading: Icon(Icons.location_on),
              title: Text(_formattedAddress),
            ),

            // Picker options
            _buildOptionCard(
              icon: Icons.map,
              title: "Standard Picker",
              onTap: () => _openPicker(standardConfig),
            ),
            // More options...
          ],
        ),
      ),
    );
  }

  void _openPicker(MapPickerConfig config) async {
    await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => MapLocationPicker(
          config: config.copyWith(
            initialPosition: _pickedLocation,
            onNext: (result) {
              if (result != null) {
                setState(() {
                  _pickedLocation = LatLng(
                    result.geometry.location.lat,
                    result.geometry.location.lng,
                  );
                  _formattedAddress = result.formattedAddress ?? "";
                });
              }
            },
          ),
          searchConfig: PlacesAutocompleteConfig(
            apiKey: YOUR_API_KEY,
          ),
        ),
      ),
    );
  }
}

πŸ’° Support the Project #

BuyMeACoffee PayPal GitHub Sponsors

πŸ‘¨β€πŸ’» Contribute #

We welcome contributions! Please see our contribution guidelines.

πŸ‘₯ Contributors #