google_maps_places_autocomplete_widgets_native
Native Places SDK (Android/iOS) backend for
google_maps_places_autocomplete_widgets.
It provides NativePlaceApiProvider, a drop-in
PlaceApiProvider
that routes autocomplete/details calls through Google's native Places SDKs
instead of REST — unlocking the two strongest client-side key protections:
- App-restricted API keys with zero configuration — the native SDKs automatically attach your app's package name / SHA-1 (Android) or bundle id (iOS), so keys restricted to your app in the Cloud console just work. No spoofable HTTP headers involved.
- Firebase App Check attestation (opt-in) — Play Integrity / App Attest tokens accompany every Places request. With enforcement enabled in the Cloud console, a scraped API key becomes useless outside your genuine app.
Where this fits (key-security tiers)
| Tier | Approach | Protection |
|---|---|---|
| 1 | Core package REST + androidPackageName/iosBundleId headers |
Deterrent only — headers are public info and spoofable |
| 2 | This package (native SDK, app-restricted key) | Restriction checked by Google from SDK-attached app identity |
| 3 | This package + App Check enforcement | Cryptographic app attestation; scraped keys stop working |
| 4 | Backend proxy holding the key server-side | Strongest; key never ships in the app |
Use the core package alone for web/desktop (the native SDKs only exist on
Android and iOS — check NativePlaceApiProvider.isSupported).
Quick start
dependencies:
google_maps_places_autocomplete_widgets: ^2.1.0
google_maps_places_autocomplete_widgets_native: ^1.0.0
import 'package:google_maps_places_autocomplete_widgets/address_autocomplete_widgets.dart';
import 'package:google_maps_places_autocomplete_widgets_native/google_maps_places_autocomplete_widgets_native.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await NativePlaceApiProvider.initialize(
mapsApiKey: 'YOUR-ANDROID/IOS-RESTRICTED-KEY',
useAppCheck: false, // true after the App Check walkthrough below
);
runApp(const MyApp());
}
// Then anywhere in your widget tree — note: no mapsApiKey parameter needed,
// the provider owns the key (requires core >= 2.1.0):
AddressAutocompleteTextField(
placeApiProvider: NativePlaceApiProvider(
componentCountry: 'us',
language: 'en-US',
),
onSuggestionClick: (place) => print(place.formattedAddress),
)
Everything else about the widgets (callbacks, styling, Place fields,
type/types filters) is identical to the core package — the same shared
mapping code parses results, so both backends return identical Place
objects.
Android setup
minSdkVersion 24or higher.- Your API key should be application-restricted to your Android app (package name + SHA-1) and API-restricted to "Places API (New)" in the Cloud console credentials page.
- No manifest changes needed — the key is supplied at runtime via
NativePlaceApiProvider.initialize.
iOS setup
- iOS 16+ (floor of the GooglePlaces 10.x SDK).
- CocoaPods:
pod installpicks upGooglePlacesautomatically. Swift Package Manager builds are also supported. - Restrict your key to your iOS bundle id + "Places API (New)".
Firebase App Check walkthrough (recommended)
App Check is why this package exists: with enforcement on, Places API calls require a valid attestation token from your genuine app, so a key scraped from your binary or network traffic is worthless. Steps:
-
Create (or reuse) a Firebase project at console.firebase.google.com — it MUST be the same Google Cloud project that owns your Places API key. App Check tokens are project-scoped: if Firebase lives in a different project than the key, enforcement will reject your app's tokens as invalid and App Check metrics will never appear. In the "Create a project" flow, select your existing Cloud project from the name field's dropdown — typing a new name silently creates a separate project.
-
Register your apps: add your Android app (package name and SHA-256 fingerprint) and your iOS app (bundle id).
-
Add the config files to your host app:
android/app/google-services.jsonandios/Runner/GoogleService-Info.plist. -
Add the Firebase Flutter plugins to your app:
dependencies: firebase_core: ^4.0.0 firebase_app_check: ^0.4.0 -
Activate App Check providers before initializing the Places provider:
await Firebase.initializeApp(); await FirebaseAppCheck.instance.activate( providerAndroid: const AndroidPlayIntegrityProvider(), providerApple: const AppleAppAttestProvider(), ); await NativePlaceApiProvider.initialize( mapsApiKey: yourKey, useAppCheck: true, ); -
Register Places API with App Check in the Firebase console (App Check → APIs → Places API) and monitor metrics until you see your real traffic attested.
-
Enable enforcement for Places API in the Firebase console once metrics look healthy.
-
Verify: a raw
curlagainstplaces.googleapis.comwith your key should now be rejected, while your app keeps working.
Debug builds: use AndroidDebugProvider() / AppleDebugProvider() and add the
printed debug token in the Firebase console (App Check → Apps → Manage debug
tokens), otherwise emulator/simulator traffic is rejected once enforcement is
on. Debug secrets are stored per app install per Firebase project — if you
re-point google-services.json at a different project, a new secret is
minted and must be registered.
Troubleshooting:
ExchangeDebugToken ... blocked/ App Check token fetch fails with 403: the API key in yourgoogle-services.jsonhas API restrictions that don't include Firebase. Add Firebase App Check API and Firebase Installations API to that key's allowed APIs (Cloud console → Credentials). This commonly happens when Firebase reuses an existing Android-restricted key that was locked down to Places API (New) only.- "Firebase App Check token is invalid" under enforcement, even from your real app: your Firebase project and your API key's Cloud project are not the same project (see step 1).
- Enforcement changes take a few minutes to propagate (~2–8 min observed).
Platform support
| Platform | Supported | Notes |
|---|---|---|
| Android | ✅ (minSdk 24) | Places SDK (New) 5.x |
| iOS | ✅ (iOS 16+) | GooglePlaces SDK 10.x |
| Web / desktop | ❌ | Use the core package's REST backend (isSupported is false) |
Limitations
Same parity notes as the core package's Places API (New) REST backend:
Suggestion.termsis alwaysnull(no native SDK equivalent).AutoCompleteType.addressis expanded tostreet_address+premise+subpremise(the new Places API has noaddresscollection); useAutoCompleteType.geocodefor broader matches.- The
languageparameter affects the details request per the device SDK's locale rules; the native SDKs primarily follow the device locale.
Development notes
Billing session tokens are managed natively: one
AutocompleteSessionToken/GMSAutocompleteSessionToken is created lazily on
the first prediction request and consumed (discarded) by the details fetch —
matching Google's billing-session semantics exactly like the core backends.