Customizing the theme of Google Maps in a Flutter app allows you to match the map’s appearance to your app’s design, such as light/dark modes or branded styles. This is achieved using the google_maps_flutter package, which supports built-in map types and custom JSON-based styles. Below is a step-by-step guide covering setup, basic theming, custom styles, and dynamic theme switching.
Prerequisites
- A Flutter project set up (run
flutter create my_appif starting fresh). - A Google Maps API key from the Google Cloud Console. Enable the Maps SDK for Android/iOS.
- Add the key to
android/app/src/main/AndroidManifest.xml(inside<application>):xml <meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_API_KEY"/> - Add it to
ios/Runner/Info.plist(as a string keyGoogleMapsApiKey).
- Step 1: Add the Dependency
In your pubspec.yaml, add:
dependencies:
flutter:
sdk: flutter
google_maps_flutter: ^2.7.0 # Use the latest version
Run flutter pub get to install.
Step 2: Basic Map Implementation
Create a simple map widget in a Dart file (e.g., lib/map_screen.dart). This sets up the controller needed for theming.
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:ui' as ui; // For Brightness enum
class MapScreen extends StatefulWidget {
@override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
late GoogleMapController _mapController;
String? _mapStyle; // For custom styles
@override
void initState() {
super.initState();
_loadMapStyle(); // Load custom style if needed
}
Future<void> _loadMapStyle() async {
// Example: Load from assets (detailed below)
_mapStyle = await DefaultAssetBundle.of(context).loadString('assets/map_styles/custom.json');
}
@override
Widget build(BuildContext context) {
final ui.Brightness brightness = MediaQuery.of(context).platformBrightness;
return Scaffold(
appBar: AppBar(title: const Text('Google Maps Theme')),
body: GoogleMap(
onMapCreated: (GoogleMapController controller) {
_mapController = controller;
_applyMapStyle(brightness); // Apply theme
},
initialCameraPosition: const CameraPosition(
target: LatLng(37.7749, -122.4194), // San Francisco
zoom: 12.0,
),
mapType: MapType.normal, // Built-in type (change as needed)
),
);
}
void _applyMapStyle(ui.Brightness brightness) {
if (_mapStyle != null) {
_mapController.setMapStyle(_mapStyle!);
}
// For dynamic dark/light, update style based on brightness
}
@override
void dispose() {
_mapController.dispose();
super.dispose();
}
}
Run the app with flutter run to see a basic map.
Step 3: Using Built-in Map Themes
For quick theming without custom JSON, use the mapType property in GoogleMap. Options include:
MapType.normal: Standard roadmap (default).MapType.satellite: Satellite imagery.MapType.terrain: Terrain view.MapType.hybrid: Satellite with labels.MapType.none: No map tiles (for overlays).
Update the GoogleMap widget:
GoogleMap(
mapType: MapType.terrain, // Or switch dynamically
// ... other properties
)
To switch dynamically (e.g., via a button):
FloatingActionButton(
onPressed: () => _mapController.setMapType(MapType.satellite),
child: const Icon(Icons.layers),
)
Step 4: Applying Custom Static Map Styles
For branded or unique looks, use JSON styles generated from tools like the Google Maps Styling Wizard (pre-built themes like “Night” or “Aubergine” available) or Snazzy Maps.
- Generate the Style:
- Visit the Styling Wizard, customize elements (e.g., roads, water, labels), and click “Finish” to get JSON.
- Add as Asset:
- Create
assets/map_styles/custom.jsonand paste the JSON (example below). - Update
pubspec.yaml:yaml flutter: assets: - assets/map_styles/ - Run
flutter pub get.
- Apply in Code:
- Load the JSON in
initState()(as shown in Step 2’s_loadMapStyle). - In
onMapCreated, call_mapController.setMapStyle(_mapStyle!);.
Example JSON for a muted style (assets/map_styles/custom.json):
[
{
"featureType": "all",
"elementType": "labels",
"stylers": [{ "visibility": "off" }]
},
{
"featureType": "road",
"elementType": "all",
"stylers": [{ "saturation": -100 }, { "lightness": 50 }]
},
{
"featureType": "water",
"elementType": "all",
"stylers": [{ "color": "#beecff" }]
}
]
Step 5: Dynamic Theme Switching (Dark/Light Mode)
To sync the map with your app’s theme (e.g., system dark mode), generate separate JSONs for light/dark and switch based on MediaQuery.platformBrightness.
- Generate Styles:
- Create
assets/map_styles/light.jsonandassets/map_styles/dark.jsonusing the Styling Wizard (e.g., “Light Monochrome” and “Night” themes).
- Load Both Styles:
UpdateinitState():
String? _lightStyle, _darkStyle;
Future<void> _loadMapStyles() async {
_lightStyle = await DefaultAssetBundle.of(context).loadString('assets/map_styles/light.json');
_darkStyle = await DefaultAssetBundle.of(context).loadString('assets/map_styles/dark.json');
}
- Apply Dynamically:
In_applyMapStyle:
void _applyMapStyle(ui.Brightness brightness) {
final String style = brightness == ui.Brightness.dark ? _darkStyle! : _lightStyle!;
_mapController.setMapStyle(style);
}
- Listen for theme changes by wrapping your app in
ThemeProvideror usingWidgetsBindingObserverto rebuild on brightness changes. - Call
_applyMapStyleindidChangeDependenciesfor reactivity:dart @override void didChangeDependencies() { super.didChangeDependencies(); final brightness = MediaQuery.of(context).platformBrightness; _applyMapStyle(brightness); }
Tips and Troubleshooting
- Styles apply only after
onMapCreated; ensure the controller is ready. - Test on physical devices/simulators, as web previews may differ.
- For more styles, explore Snazzy Maps and copy-paste JSON directly.
- If styles don’t load, check asset paths and console for errors.
This setup ensures your map integrates seamlessly with your app’s UI. For full examples, check the google_maps_flutter pub.dev page.
Where do these light/dart.json come from
Those light.json and dark.json files are example asset files I referenced in the Flutter code for custom Google Maps styling—they’re not built-in or automatically generated by Flutter or the google_maps_flutter package. Instead, they’re user-created JSON files containing map style rules that you (the developer) generate yourself using free online tools. Here’s a quick breakdown of their origin and how to create them:
What They Represent
- These are custom map style definitions in JSON format, based on Google Maps’ styling API. Each file defines visual tweaks like colors, visibility, saturation, and lightness for map elements (e.g., roads, water, labels, parks).
light.json: A light-themed style (e.g., brighter colors, higher contrast for daytime use).dark.json: A dark-themed style (e.g., muted tones, inverted colors for night mode).- They’re stored in your app’s
assets/map_styles/folder and loaded at runtime to apply the theme via_mapController.setMapStyle().
Where to Get/Create Them
You create these from scratch using Google’s official tools or community libraries. No download is needed; just copy-paste the generated JSON into your project files. Recommended sources:
- Google Maps Styling Wizard (Official Tool):
- Go to mapstyle.withgoogle.com.
- Customize the map interactively: Select elements (e.g., roads, water) from the sidebar, adjust colors/hue/saturation/lightness, and preview live.
- For light theme: Start with a default or “Pale Dawn” base, then tweak for subtlety (e.g., desaturate grays for roads, light blue for water).
- For dark theme: Choose “Night” or “Midnight Commander” as a starting point, invert colors (e.g., dark grays for land, deep navy for water).
- Click “Finish” > “Copy JSON” to get the raw code.
- Paste it into
light.jsonordark.jsonin yourassets/folder. Examplelight.jsonoutput (simplified for a clean, minimalist light theme):
[
{
"featureType": "all",
"elementType": "geometry",
"stylers": [{ "color": "#f5f5f5" }]
},
{
"featureType": "road",
"elementType": "geometry",
"stylers": [{ "color": "#ffffff" }]
},
{
"featureType": "water",
"elementType": "geometry",
"stylers": [{ "color": "#c8d7d0" }]
},
{
"featureType": "landscape",
"stylers": [{ "color": "#e0e0e0" }]
}
]
Example dark.json output (simplified for a sleek dark mode):
[
{
"featureType": "all",
"elementType": "geometry",
"stylers": [{ "color": "#212121" }]
},
{
"featureType": "road",
"elementType": "geometry",
"stylers": [{ "color": "#424242" }]
},
{
"featureType": "water",
"elementType": "geometry",
"stylers": [{ "color": "#1f3543" }]
},
{
"featureType": "landscape",
"stylers": [{ "color": "#2c3e50" }]
}
]
- Snazzy Maps (Community Gallery):
- Visit snazzymaps.com and browse/search for “light” or “dark” themes (e.g., “Clean Light” or “Dark Monochrome”).
- Click on a style > “Edit this map” or “Show JSON code.”
- Copy the JSON and save it as your
.jsonfile. - Pro: Hundreds of pre-made, tested styles. Con: Less customization than Google’s wizard.
Next Steps in Your Project
- After creating the files, ensure they’re declared in
pubspec.yaml(as I mentioned before). - Test by running the app and toggling themes—Google Maps will render the styles instantly.
- If you want more advanced options (e.g., runtime generation), you could fetch styles from a server, but assets are simplest for static themes.
Making Google Maps Theme Light-Only in Flutter
Yes, absolutely! You can easily configure your Flutter app to always use a light theme for the Google Map, ignoring the system’s dark mode or any dynamic brightness changes. This is ideal if your app has a fixed light UI or you want a consistent map appearance. We’ll build on the previous code by simplifying the style loading and application—loading only the light.json and applying it unconditionally.
Quick Setup Recap
- Ensure you have
assets/map_styles/light.jsonwith your light theme JSON (generated from Google’s Styling Wizard or Snazzy Maps, as discussed). - Declare it in
pubspec.yamlunderflutter: assets: - assets/map_styles/.
Updated Code: Light-Only Implementation
In your MapScreen (or equivalent), remove dark mode logic and hardcode the light style. Here’s the modified _MapScreenState:
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class MapScreen extends StatefulWidget {
@override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
late GoogleMapController _mapController;
String? _lightStyle; // Only light style now
@override
void initState() {
super.initState();
_loadLightStyle(); // Load only light style
}
Future<void> _loadLightStyle() async {
_lightStyle = await DefaultAssetBundle.of(context).loadString('assets/map_styles/light.json');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Light-Only Google Maps Theme')),
body: GoogleMap(
onMapCreated: (GoogleMapController controller) {
_mapController = controller;
_applyLightStyle(); // Always apply light style
},
initialCameraPosition: const CameraPosition(
target: const LatLng(37.7749, -122.4194), // Example: San Francisco
zoom: 12.0,
),
mapType: MapType.normal, // Built-in type; custom style overrides visuals
),
);
}
void _applyLightStyle() {
if (_lightStyle != null) {
_mapController.setMapStyle(_lightStyle!);
}
}
@override
void dispose() {
_mapController.dispose();
super.dispose();
}
}
Key Changes and Why They Work
- Single Style Load:
_loadLightStyle()fetches onlylight.json. No need for_darkStyleor conditional checks. - Unconditional Application:
_applyLightStyle()always sets the light style inonMapCreated. It ignoresMediaQuery.platformBrightnessentirely. - No Dynamic Updates: Removed
didChangeDependenciesor any listeners for theme changes, so the map stays light even if the user toggles system dark mode. - Fallback to Built-in: If you skip custom JSON, just set
mapType: MapType.normalfor Google’s default light roadmap—it’s light by nature and doesn’t require assets.
Testing and Tips
- Run
flutter runon a device/simulator. The map should render in your light style immediately after loading. - If you want to force even the built-in types to feel “lighter,” combine with
mapType: MapType.normalorMapType.terrain. - Performance: Loading from assets is fast and offline-friendly. For a truly minimal light theme, use a simple JSON like the one I provided earlier (e.g., light grays and whites).
- Edge Case: On very old Android devices, styles might not apply smoothly—test on your target SDKs.
