Flutter application featuring Google Maps integration with real-time location tracking, place search, and custom map styling. Built with Bloc pattern for state management and supporting Android, iOS, and Web platforms.
Before getting started, make sure you have the following installed:
- Flutter SDK: >=3.10.0 <4.0.0
- Dart SDK: >=3.10.0 <4.0.0
- IDE: VSCode or Android Studio with Flutter extensions
- Google Cloud Account: Required for Google Maps API access
- Platforms:
- For iOS: Xcode (macOS only)
- For Android: Android Studio or Android SDK
- For Web: Google Chrome
git clone <repository-url>
cd flutter_maps_appflutter pub getThis project requires Google Maps API keys for Android, iOS, and Web.
- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the following APIs:
- Maps SDK for Android
- Maps SDK for iOS
- Maps JavaScript API
- Places API
- Directions API
- Geocoding API
- Go to APIs & Services > Credentials
- Click Create Credentials > API Key
- Create separate API keys for each platform (recommended) or use one key for all
- Restrict each API key to specific APIs and platforms for security
- Restrict the Android API key to:
- Maps SDK for Android
- Places API
- Android apps (add your app's SHA-1 fingerprint)
- Restrict the iOS API key to:
- Maps SDK for iOS
- Places API
- iOS apps (add your bundle identifier)
-
Restrict the Web API key to:
- Maps JavaScript API
- Places API
- Directions API
- Geocoding API
- HTTP referrers (add your website URLs)
-
The Web platform uses
--dart-defineto pass the API key securely:- The key is loaded dynamically at runtime via Dart code
- Never hardcode the API key in
web/index.html - See the Final Recommendations section for detailed security practices
The necessary permissions are already configured in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>Add location permissions to ios/Runner/Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to your location to show your position on the map</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to your location to track your position</string>The project uses flutter_gen to generate type-safe access to assets:
flutter pub run build_runner build --delete-conflicting-outputsAssets are located at:
assets/map_styles/- Custom map style JSON filesassets/images/- Image assets
flutter gen-l10nImportant: Always pass the Google Maps API key using --dart-define:
flutter run --dart-define-from-file=config-keys.json# Android
flutter run -d android --dart-define-from-file=config-keys.json
# iOS
flutter run -d iPhone --dart-define-from-file=config-keys.json
# Web (development)
flutter run -d chrome --dart-define-from-file=config-keys.json# Android (App Bundle)
flutter build appbundle --dart-define-from-file=config-keys.json
# Android (APK)
flutter build apk --dart-define-from-file=config-keys.json
# iOS
flutter build ios --dart-define-from-file=config-keys.json
# Web (production - use production key with domain restrictions)
flutter build web --dart-define-from-file=config-keys.jsonlib/
├── core/ # Core application infrastructure
│ ├── gen/ # Generated assets (flutter_gen)
│ ├── theme/ # App theming
│ └── utils/ # Utilities and helpers
├── Bloc/ # Bloc state management
│ ├── location_Bloc/ # User location tracking
│ ├── map_Bloc/ # Map state management
│ └── places_Bloc/ # Places search
├── data/ # Data layer
│ ├── models/ # Data models
│ ├── repositories/ # Data repositories
│ └── services/ # External services (Google Maps API)
├── ui/ # UI layer
│ ├── pages/ # Application pages
│ └── widgets/ # Reusable widgets
└── main.dart # Application entry point
assets/
├── map_styles/ # Custom map styles (JSON)
└── images/ # Image assets
- Interactive Map: Full-featured Google Maps with gestures support
- Map Controls: Zoom, compass, and map type controls
- Custom Markers: Add custom markers with info windows
- Polylines & Polygons: Draw routes and areas on the map
- Map Styling: Apply custom map styles (light, dark, retro, etc.)
- Map Types: Switch between normal, satellite, hybrid, and terrain views
- Real-time Location: Track user's current location
- Location Updates: Continuous location tracking
- Location Permissions: Handle location permission requests
- GPS Accuracy: Display location accuracy radius
- My Location Button: Quick navigation to current position
- Background Tracking: Continue tracking in background (optional)
- Place Search: Search for places using Google Places API
- Autocomplete: Real-time search suggestions
- Place Details: View detailed information about places
- Nearby Places: Find nearby points of interest
- Place Categories: Filter by category (restaurants, gas stations, etc.)
- Custom Markers: Display search results on map
- Custom Styles: Multiple pre-defined map styles
- Style Switcher: Easy switching between map styles
- Custom Markers: Icon customization for different marker types
- Info Windows: Custom info window designs
- Smooth Animations: Animated camera movements
- Loading States: Skeleton loaders and progress indicators
- Error Handling: User-friendly error messages
- Offline Support: Basic functionality without internet
- State Persistence: Remember last map position and settings
- Responsive Design: Works on phones and tablets
Manages user location tracking:
LocationRequested: Request current locationLocationUpdated: Continuous location updatesLocationPermissionChanged: Handle permission changesLocationServiceStatusChanged: Track GPS service status
Manages map state and interactions:
MapInitialized: Initialize map with settingsMapStyleChanged: Apply custom map styleMapTypeChanged: Switch map type (normal, satellite, etc.)CameraPositionChanged: Update camera positionMarkerAdded: Add marker to mapPolylineAdded: Add route/path to map
Handles place search functionality:
PlacesSearchRequested: Search for placesPlaceSelected: Show details for selected placeNearbyPlacesRequested: Find nearby placesAutocompleteQueryChanged: Update search suggestions
- Android: Uses Google Maps SDK for Android
- iOS: Uses Google Maps SDK for iOS
- Web: Uses Google Maps JavaScript API
Search and autocomplete functionality:
GET https://maps.googleapis.com/maps/api/place/autocomplete/json?input={query}&key={API_KEY}&types={types}&location={lat,lng}&radius={radius}
Convert addresses to coordinates:
GET https://maps.googleapis.com/maps/api/geocode/json?address={address}&key={API_KEY}
Custom map styles are defined in JSON format in assets/map_styles/. Each style file follows
the Snazzy Maps format.
- night_blue_mode.json - Grayscale monochrome style
- night_mode.json - Vintage map appearance
- personal_mode.json - Dark theme for low-light environments
- uber_mode.json - High contrast night mode
flutter testflutter test --coverage# Generate coverage report
genhtml coverage/lcov.info -o coverage/
# Open coverage report in browser
open coverage/index.htmlflutter test test/path/to/test_file.dartThe project uses very_good_analysis to maintain code quality:
flutter analyzeflutter format .For assets and localization:
# Generate assets
flutter pub run build_runner build --delete-conflicting-outputs
# Watch for changes and auto-generate
flutter pub run build_runner watch --delete-conflicting-outputs
# Generate localization
flutter gen-l10n- google_maps_flutter: Google Maps SDK integration
- geolocator: Location services and permissions
- google_maps_webservice: Google Maps Web Services API
- flutter_Bloc: Bloc pattern implementation
- Bloc: Core Bloc library
- equatable: Value equality for Bloc states
- Bloc_concurrency: Advanced Bloc concurrency control
- shared_preferences: Local key-value storage
- stream_transform: Stream transformation utilities
- build_runner: Code generation
- flutter_gen_runner: Type-safe asset generation
- very_good_analysis: Strict lint rules
The app requests the following location permissions:
ACCESS_FINE_LOCATION: Precise location from GPSACCESS_COARSE_LOCATION: Approximate location from network
- When In Use: Location access while app is in foreground
- Always: Location access even when app is in background (if needed)
Web uses browser's Geolocation API, which prompts user for permission automatically.
- Check API Key: Verify API key is correctly configured
- Enable APIs: Ensure required APIs are enabled in Google Cloud Console
- Billing: Verify billing is enabled for your Google Cloud project
- Restrictions: Check API key restrictions aren't blocking requests
- Platform: Confirm API key is configured for the correct platform
- Verify location permissions in
AndroidManifest.xml - Check device GPS is enabled
- Grant location permission in app settings
- Test on physical device (emulator GPS may be unreliable)
- Check
Info.plisthas location usage descriptions - Verify location permissions granted in iOS Settings
- Test on physical device for best results
- Use HTTPS (required for geolocation on web)
- Allow location permission in browser
- Check browser console for geolocation errors
- Verify API key is valid
- Check internet connection
- Ensure Maps SDK is enabled for your platform
- Check for JavaScript errors (Web)
- Verify billing is enabled
- Enable Places API in Google Cloud Console
- Check API key has Places API access
- Verify internet connection
- Check API quota and billing
- Copy API key exactly (no extra spaces)
- Check API key restrictions match your app
- Verify correct API is enabled
- Try creating a new unrestricted key for testing
flutter clean
cd ios
pod install
pod update
cd ..
flutter build iosflutter clean
cd android
./gradlew clean
cd ..
flutter build apkflutter clean
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputsHandle permission denial gracefully:
- Show explanation dialog
- Direct user to app settings
- Provide fallback functionality
- Test permission flows thoroughly
This app implements several performance optimizations:
- Marker Clustering: Group nearby markers to reduce clutter
- Visible Region: Only render markers in visible area
- Lite Mode: Use lite mode for static maps
- Zoom-based Markers: Show/hide markers based on zoom level
The app follows the Bloc (Business Logic Component) pattern:
- UI: Widgets that display data and emit events
- Bloc: Business logic that processes events and emits states
- Repository: Data layer that communicates with APIs and services
- Services: External service integrations (Google Maps, Location)
- User interacts with map (pan, zoom, search)
- UI emits event to appropriate Bloc
- Bloc processes event and calls repository/service
- Service interacts with Google Maps API or device GPS
- Repository returns data to Bloc
- Bloc emits new state
- UI rebuilds to reflect changes
- GoogleMapsService: Wrapper for Google Maps Web Services
- LocationService: Handles device location tracking
- PlacesService: Place search and autocomplete
- GeocodeService: Address to coordinate conversion
- Restrict Keys: Always restrict API keys to specific APIs and platforms
- Separate Keys: Use different keys for dev/staging/production
- Don't Commit: Never commit API keys to version control
- Environment Variables: Use environment variables or secret management
- Rotation: Regularly rotate API keys
- Monitoring: Monitor API usage for anomalies
Important: For client-side applications (web), the Google Maps API key will be visible in the compiled JavaScript. This is normal and expected for frontend apps. Google provides proper security mechanisms to protect your key:
Application Restrictions (Critical):
1. Go to Google Cloud Console → APIs & Services → Credentials
2. Select your Web API key
3. Under "Application restrictions", choose "HTTP referrers (websites)"
4. Add your authorized domains:
- https://yourdomain.com/*
- https://*.yourdomain.com/*
- http://localhost:* (for development only)
API Restrictions (Required):
Enable ONLY the APIs you use:
- Maps JavaScript API
- Places API
- Directions API
- Geocoding API
Development:
{
"GOOGLE_MAPS_API_KEY": "dev_key_with_localhost"
}# Run with development key (restricted to localhost)
flutter run -d chrome --dart-define-from-file=config-keys.jsonProduction:
{
"GOOGLE_MAPS_API_KEY": "prod_key_with_domain_restrictions"
}# Build with production key (restricted to your domain)
flutter build web --dart-define-from-file=config-keys.json- In Google Cloud Console, go to "APIs & Services" → "Quotas"
- Set daily request limits for each API
- Configure billing alerts to notify you of unusual usage
- Review usage reports regularly in the Dashboard
- Enable detailed logging in Google Cloud Console
- Set up alerts for unexpected usage spikes
- Review the "APIs & Services → Dashboard" weekly
- Check for unauthorized domains in the logs
- The API key being visible in JavaScript is by design for client-side APIs
- Security comes from domain restrictions and API restrictions, not from hiding the key
- Even if someone copies your key, they cannot use it from unauthorized domains
- Usage quotas prevent abuse and unexpected charges
- This is the official Google-recommended approach for web applications
[Include license information here]