This tutorial walks you through building a Flutter application that integrates the Magic Lane Maps SDK to display an interactive map. The application demonstrates proper SDK integration and state management with Provider.
- Prerequisites
- Project Setup
- Architecture Overview
- Integrate the SDK
- Implementation
- Running the Application
- Troubleshooting
- Flutter SDK (^3.9.2)
- Dart development environment
- Magic Lane API token (see Getting Started Guide)
- IDE with Flutter support (VS Code, Android Studio, etc.)
flutter create flutter_test_demo
cd flutter_test_demoAdd the required dependencies to your pubspec.yaml:
name: flutter_test_demo
description: "A new Flutter project."
publish_to: 'none'
version: 0.1.0
environment:
sdk: ^3.9.2
dependencies:
flutter:
sdk: flutter
magiclane_maps_flutter: ^3.1.2
provider: ^6.1.5+1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
flutter:
uses-material-design: trueNote
It is recommended to use the latest magiclane_maps_flutter package version available.
flutter pub getThe application follows a clear separation of concerns with the following structure:
lib/
├── main.dart # Entry point and main app configuration
├── providers/
│ ├── app_providers.dart # Provider configuration
│ └── gem_map_provider.dart # Map controller state management
└── widgets/
└── app_map.dart # Map widget implementation
Follow these ordered steps from the official Magic Lane Maps SDK for Flutter documentation to integrate the SDK and make the map display work correctly.
You can check for the latest version on pub.dev.
- Android native configuration
Update the Android project build.gradle.kts to include the Magic Lane Maven repository and ensure release builds do not shrink resources (the SDK requires native assets to remain available):
Add the Maven repository (e.g., in android/build.gradle.kts or android/build.gradle):
allprojects {
repositories {
google()
mavenCentral()
maven {
url = uri("https://developer.magiclane.com/packages/android")
}
}
}And in app/build.gradle.kts ensure release build types don't minify/shrink resources (adjust signing as needed):
buildTypes {
release {
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = false
isShrinkResources = false
}
}- iOS native configuration
- Open the iOS Xcode project and set the minimum deployment target to iOS 14.0 or higher.
- Ensure the Info.plist contains the required development region key to avoid release-mode crashes:
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>If you modify platform-specific files, run the usual Flutter platform setup commands:
flutter clean
flutter pub getFirst, create the provider for managing the map controller state:
import 'package:flutter/material.dart' hide Route; // Avoid name conflict with Magic Lane SDK Route class
import 'package:magiclane_maps_flutter/magiclane_maps_flutter.dart';
// Provider for managing the GemMapController instance
class GemMapProvider extends ChangeNotifier {
GemMapController? _controller;
GemMapController? get controller => _controller;
void setController(GemMapController controller) {
_controller = controller;
notifyListeners();
}
void clearController() {
_controller = null;
notifyListeners();
}
}Key Points:
- Extends
ChangeNotifierfor reactive state management - Manages the
GemMapControllerinstance lifecycle - Provides methods to set and clear the controller
- Notifies listeners when the controller state changes
class AppProviders extends StatelessWidget {
final Widget child;
const AppProviders({super.key, required this.child});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [ChangeNotifierProvider(create: (_) => GemMapProvider())],
child: child,
);
}
}Key Points:
- Wraps the app with
MultiProviderfor dependency injection - Creates and provides the
GemMapProviderinstance - Allows any child widget to access the map provider
import 'package:flutter/material.dart';
import 'package:magiclane_maps_flutter/magiclane_maps_flutter.dart';
import 'package:provider/provider.dart';
class AppMap extends StatefulWidget {
final String appAuthorization;
const AppMap({super.key, required this.appAuthorization});
@override
State<AppMap> createState() => _AppMapState();
}
class _AppMapState extends State<AppMap> {
void _onMapCreated(GemMapController controller) {
WidgetsBinding.instance.addPostFrameCallback((_) {
final provider = context.read<GemMapProvider>();
provider.setController(controller);
});
setState(() {});
}
@override
void dispose() {
GemKit.release();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GemMap(onMapCreated: _onMapCreated, appAuthorization: widget.appAuthorization);
}
}Key Points:
StatefulWidgetto manage map lifecycleGemMapwidget represents the displayed map_onMapCreatedcallback handles controller initialization- Uses
WidgetsBinding.instance.addPostFrameCallbackfor safe provider access - Properly releases GemKit resources in
dispose() - Authorization token for prioritized map services
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
/// Global navigator key for accessing navigation context from anywhere in the app
static final navigatorKey = GlobalKey<NavigatorState>();
/// Project API token for map services authentication
static const projectApiToken = String.fromEnvironment('GEM_TOKEN');
@override
Widget build(BuildContext context) {
return AppProviders(
child: MaterialApp(navigatorKey: navigatorKey, home: const MapPage()),
);
}
}
class MapPage extends StatefulWidget {
const MapPage({super.key});
@override
State<MapPage> createState() => _MapPageState();
}
class _MapPageState extends State<MapPage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(children: [AppMap(appAuthorization: MainApp.projectApiToken)]),
);
}
}Key Points:
- Uses
String.fromEnvironment('GEM_TOKEN')for secure token management - Wraps the app with
AppProvidersfor state management - Global navigator key for navigation access
ScaffoldwithStacklayout for potential overlay elementsresizeToAvoidBottomInset: falseprevents keyboard interference
The Magic Lane SDK requires an API token for authentication:
static const projectApiToken = String.fromEnvironment('GEM_TOKEN');This approach uses environment variables for security. Set the token when running:
flutter run --dart-define=GEM_TOKEN=your_api_token_hereThe GemMapController is the primary interface for map interactions:
void _onMapCreated(GemMapController controller) {
WidgetsBinding.instance.addPostFrameCallback((_) {
final provider = context.read<GemMapProvider>();
provider.setController(controller);
});
setState(() {});
}Why addPostFrameCallback?
- Ensures the widget tree is fully built before accessing providers
- Prevents potential context access issues during widget creation
- Guarantees safe provider operations
Proper cleanup is essential for preventing memory leaks:
@override
void dispose() {
GemKit.release();
super.dispose();
}export GEM_TOKEN="your_magic_lane_api_token"flutter run --dart-define=GEM_TOKEN=$GEM_TOKENOr for specific platforms:
# iOS
flutter run -d ios --dart-define=GEM_TOKEN=$GEM_TOKEN
# Android
flutter run -d android --dart-define=GEM_TOKEN=$GEM_TOKENIf everything is set up correctly, the app will display a responsive, interactive map that supports common gestures — panning, pinch-to-zoom, double-tap zoom, and two-finger shove/rotate interactions — providing a smooth and natural user experience.
- Watermark displayed over map: Verify API token is correctly set
- Build failures: Ensure all dependencies are properly installed
- Performance issues: Check resource disposal in dispose methods
# Check dependencies
flutter pub deps
# Clean and rebuild
flutter clean
flutter pub get
flutter run
# Check for issues
flutter doctorThis tutorial demonstrates how to integrate the Magic Lane Maps SDK into a Flutter application with proper architecture and best practices. The implementation provides a solid foundation for building more complex mapping applications with additional features and functionality.

