Get Started - iOS
Contents
Introduction
Welcome! This guide provides a foundational example demonstrating how to integrate Trimble’s Mobile Maps SDK into a Flutter application, specifically targeting the iOS platform. Our goal is to help you get a basic map view up and running using Flutter’s PlatformView mechanism.
Who is this guide for?
This guide is intended for developers who have some basic experience with Flutter development. We assume you are familiar with:
- Creating and running a new Flutter project.
- The general structure of a Flutter application.
- Basic Dart and Flutter concepts.
If you are new to Flutter or need a refresher, we highly recommend starting with the official Flutter ‘Get Started’ guide before proceeding.
What will you learn?
By following this guide, you will learn how to:
- Set up the necessary configurations in your iOS Flutter project to use the Trimble Maps SDK.
- Implement a basic Flutter
PlatformViewto display a Trimble Map within your Flutter UI. - Establish the initial connection between your Flutter code and the native iOS map view.
Scope and Limitations
Please note that this guide focuses solely on the initial setup and integration for displaying a map on iOS using PlatformView. It serves as a starting point and does not cover:
- The full range of features and capabilities offered by the Trimble Maps SDK (like routing, custom styling, location services integration, etc.).
- Advanced
PlatformViewcommunication techniques.
For comprehensive information on the SDK’s features and functionalities, this guide should be used in conjunction with the official Trimble Maps SDK for iOS documentation.
Let’s get started!
Set Up Dependencies
Before writing any Dart code to display the map, you need to configure the native iOS part of your Flutter application to include the Trimble Maps SDK libraries. This involves using Carthage to manage dependencies and adding the frameworks to your Xcode project.
Step 1: Create a Cartfile
Carthage uses a Cartfile to specify dependencies. You need to create this file in your iOS project directory.
-
Navigate to your iOS project directory:
ios/ -
Create a new file named
Cartfile(no extension) in theios/directory. -
Add the Trimble Maps SDK dependencies to the Cartfile:
# Latest release
binary "https://trimblemaps.jfrog.io/artifactory/carthage/TrimbleMaps.json" == 2.0.0
binary "https://trimblemaps.jfrog.io/artifactory/carthage/TrimbleMapsMobileEvents.json" == 1.0.0
binary "https://trimblemaps.jfrog.io/artifactory/carthage/TrimbleMapsAccounts.json" == 2.0.0
Note: The version numbers (2.0.0 for TrimbleMaps and 1.0.0 for TrimbleMapsMobileEvents and 2.0.0 for TrimbleMapsAccounts) provided here are examples. While they should work for this guide, you might want to check the developer portal or the Artifactory repository for the latest released versions for your production applications.
Step 2: Install Dependencies with Carthage
-
Open Terminal and navigate to your iOS project directory:
ios/ -
Run the Carthage bootstrap command to download and build the frameworks:
carthage bootstrap --platform iOS --use-xcframeworks
This command will:
- Download the Trimble Maps SDK XCFrameworks from the Artifactory repository
- Place them in the
Carthage/Build/directory
Note: Make sure you have Carthage installed. If not, install it using Homebrew: brew install carthage
Step 3: Add Frameworks to Xcode Project
-
Open your project in Xcode:
ios/Runner.xcworkspace(orios/Runner.xcodeproj) -
Select your project in the Project Navigator (the top-level item).
-
Select the Runner target.
-
Go to the General tab.
-
Scroll down to the Frameworks, Libraries, and Embedded Content section.
-
Click the + button to add frameworks.
-
Click Add Other… and then Add Files…
-
Navigate to
Carthage/Build/and select:TrimbleMaps.xcframeworkTrimbleMapsMobileEvents.xcframeworkTrimbleMapsAccounts.xcframework
-
For each framework, ensure Embed & Sign is selected in the dropdown next to it.
Configure Permissions
If you plan to display the user’s location on the map or use location services, you need to add location permissions to your app’s Info.plist file.
Step 1: Add Location Usage Description
-
Open
ios/Runner/Info.plistin Xcode (or as a text file). -
Add the following keys and values:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your precise location is used to show your location on the map.</string>
- If you need background location updates, also add:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
- For full-accuracy location access (iOS 14+), add:
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>map</key>
<string>Please enable precise location to show your location on the map.</string>
</dict>
Note: The permission strings should be customized to explain how your app uses location data. These strings are shown to users when permission is requested.
Implement the Native iOS View (TrimbleMapView.swift)
Now that the dependencies are set up, we need to create the native iOS code that will manage and display the Trimble Map. We’ll use Flutter’s PlatformView interface to embed this native view within our Flutter widget tree.
This involves creating a Swift class that:
- Initializes the Trimble Maps SDK and the native
TMGLMapView. - Manages the lifecycle of the
TMGLMapView. - Sets up a
MethodChannelto receive commands from Flutter. - Executes map actions (like zooming or changing style) based on those commands.
- Returns the actual
TMGLMapViewinstance to be displayed by Flutter.
Step 1: Create the TrimbleMapView.swift File
First, create a new Swift file named TrimbleMapView.swift within your iOS project structure. A common location is:
ios/Runner/TrimbleMapView.swift
Step 2: Add the TrimbleMapView Class Code
Paste the following Swift code into the TrimbleMapView.swift file you just created. (We’ve added extra logging in here so you can follow the workflow in your debugger.):
import Foundation
import UIKit
import Flutter
import TrimbleMaps
import TrimbleMapsAccounts
/**
* A PlatformView that wraps the Trimble Map SDK's TMGLMapView and
* exposes zoom, style, and center controls via a MethodChannel.
* It also handles TMGLMapView lifecycle events.
*/
class TrimbleMapView: NSObject, FlutterPlatformView, AccountManagerDelegate {
private var frame: CGRect
private let viewId: Int64 // Unique identifier for this map view instance
private let apiKey: String // API key for Trimble Maps services
private let region: Region // Region for Trimble Maps services
private var containerView: UIView // Container view returned to Flutter
private var mapView: TMGLMapView? // The core native Trimble MapView instance
private var channel: FlutterMethodChannel? // MethodChannel for communication between Flutter and this native view
private var isMapReady: Bool = false // Flag to track if map is ready
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger?
) {
self.viewId = viewId
self.frame = frame
// Create container view that will hold the map
self.containerView = UIView(frame: frame)
self.containerView.backgroundColor = .systemBackground
// Extract the API key and region from arguments
if let argsDict = args as? [String: Any],
let apiKey = argsDict["apiKey"] as? String {
self.apiKey = apiKey
} else {
self.apiKey = ""
print("TrimbleMapView: Warning - No API key provided")
}
// Parse region from arguments, default to worldwide
if let argsDict = args as? [String: Any],
let regionString = argsDict["region"] as? String {
switch regionString.lowercased() {
case "northamerica":
self.region = .northAmerica
case "europe":
self.region = .europe
default:
self.region = .worldwide
}
} else {
self.region = .worldwide
}
super.init()
print("TrimbleMapView: Initializing for viewId=\(viewId)")
// Initialize the MethodChannel
// The channel name includes the viewId to ensure uniqueness if multiple maps are shown.
if let messenger = messenger {
self.channel = FlutterMethodChannel(
name: "trimble_map_view_\(viewId)",
binaryMessenger: messenger
)
// Set up Method Call Handler
// This listener waits for method calls invoked from the Flutter side using the channel.
self.channel?.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
self?.handleMethodCall(call: call, result: result)
}
}
// Check if account is already loaded and licensed
if AccountManager.default.state == .loaded &&
AccountManager.default.isLicensed(licensedFeature: .mapsSdk) {
print("TrimbleMapView: Account already loaded, initializing map immediately")
initializeMapView()
} else {
// Initialize Trimble Maps Account (Authentication)
// Only set account if not already loaded
let account = Account(apiKey: self.apiKey, region: self.region)
AccountManager.default.account = account
AccountManager.default.delegate = self
}
}
// MARK: - Map Initialization
/// Creates and adds the TMGLMapView to the container view
private func initializeMapView() {
guard mapView == nil else { return } // Prevent duplicate initialization
// Create the TMGLMapView UI component
let map = TMGLMapView(frame: containerView.bounds)
map.autoresizingMask = [.flexibleWidth, .flexibleHeight]
map.styleURL = TMGLStyle.mobileDayStyleURL
containerView.addSubview(map)
self.mapView = map
self.isMapReady = true
// Notify Flutter that the map is ready to be interacted with
self.channel?.invokeMethod("mapReady", arguments: nil)
print("TrimbleMapView: Map initialized and 'mapReady' notification sent to Flutter")
}
// MARK: - AccountManagerDelegate
func stateChanged(newStatus: AccountManagerState) {
if newStatus == .loaded {
if AccountManager.default.isLicensed(licensedFeature: .mapsSdk) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
print("TrimbleMapView: Account loaded, initializing map")
self.initializeMapView()
}
} else {
print("TrimbleMapView: Account is not licensed for Maps SDK")
}
}
}
// MARK: - Method Call Handling
private func handleMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) {
print("TrimbleMapView: Received method call from Flutter: \(call.method)")
guard let mapView = self.mapView else {
result(FlutterError(code: "MAP_NOT_READY", message: "Map view is not initialized", details: nil))
return
}
switch call.method {
case "zoomIn":
// Get current zoom level and increase it
let currentZoom = mapView.zoomLevel
mapView.setZoomLevel(currentZoom + 1, animated: true)
result(nil)
print("TrimbleMapView: zoomIn executed")
case "zoomOut":
// Get current zoom level and decrease it
let currentZoom = mapView.zoomLevel
mapView.setZoomLevel(currentZoom - 1, animated: true)
result(nil)
print("TrimbleMapView: zoomOut executed")
case "toggleStyle":
// Check the current style and switch to the other
let currentStyle = mapView.styleURL
let newStyle: URL
if currentStyle == TMGLStyle.mobileDayStyleURL {
newStyle = TMGLStyle.mobileNightStyleURL
} else {
newStyle = TMGLStyle.mobileDayStyleURL
}
mapView.styleURL = newStyle
print("TrimbleMapView: toggleStyle executed, set to: \(newStyle)")
result(nil)
case "center":
// Extract arguments sent from Flutter
guard let args = call.arguments as? [String: Any],
let lat = args["lat"] as? Double,
let lng = args["lng"] as? Double else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "lat and lng must be provided", details: nil))
return
}
print("TrimbleMapView: center executing for Lat: \(lat), Lng: \(lng)")
// Create a coordinate and center the map on it
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lng)
mapView.setCenter(coordinate, zoomLevel: 8.0, animated: true)
result(nil)
default:
print("TrimbleMapView: Method not implemented: \(call.method)")
result(FlutterMethodNotImplemented)
}
}
// MARK: - FlutterPlatformView Implementation
/**
* Returns the container UIView that Flutter will embed.
* The TMGLMapView is added to this container once the account is licensed.
*/
func view() -> UIView {
return containerView
}
// MARK: - Cleanup
deinit {
print("TrimbleMapView: Cleaning up MethodChannel and MapView")
// Remove the method call handler to break the communication link.
channel?.setMethodCallHandler(nil)
// Note: TMGLMapView cleanup is handled automatically by iOS
}
}
Step 3: Review the Code
- Class Definition:
TrimbleMapViewimplementsFlutterPlatformView(required by Flutter to get the nativeUIView) andAccountManagerDelegate(to handle account initialization).- The initializer receives
frame,viewId(unique ID for this view instance),args(creation parameters from Flutter including API key and region), andbinaryMessenger(forMethodChannel).
containerView: UIView: A container view that is immediately returned to Flutter. The map view is added to this container only after the account is licensed.mapView: TMGLMapView?: This holds the actual instance of the Trimble MapsTMGLMapViewwidget.channel: FlutterMethodChannel?: This is the communication bridge. Note the channel nametrimble_map_view_\(viewId)– using theviewIdensures that if you had multiple map views, each would have its own distinct channel.- Initialization:
- Container View: A container
UIViewis created immediately and will be returned by theview()function. - Account Check: Before setting up a new account, we check if
AccountManager.default.stateis already.loadedand licensed. If so, we initialize the map immediately. Otherwise, we set up the account and wait for the delegate callback. initializeMapViewMethod: Creates theTMGLMapViewand adds it to the container view. This is called either immediately (if already licensed) or whenstateChangedreports the account is loaded.stateChangedMethod: This delegate method is called when the account status changes. When the status is.loadedand the account is licensed for Maps SDK, we callinitializeMapView().handleMethodCallMethod: This is the listener for incoming messages from Flutter.- The
switchstatement checks the name of the method invoked by Flutter (e.g.,"zoomIn","toggleStyle"). - Each case handles a specific map action.
- The
- Container View: A container
view() -> UIView: Implements theFlutterPlatformViewrequirement, returning thecontainerViewso Flutter can display it immediately while the map initializes.deinit: Cleanup method that removes the method channel handler when the view is deallocated. This prevents memory leaks.
With this class created, you have the core native component ready. The next step is to work on the TrimbleMapViewFactory so that Flutter can use it to create instances of this TrimbleMapView.
Create the PlatformView Factory (TrimbleMapViewFactory.swift)
Flutter needs a way to create instances of the TrimbleMapView you defined earlier whenever it needs to display the map widget. This is done using a FlutterPlatformViewFactory.
The factory is a relatively simple class whose primary responsibility is to receive creation parameters from Flutter (like your API key and region) and use them to instantiate and return a new TrimbleMapView.
Step 1: Create the TrimbleMapViewFactory.swift File
Create a new Swift file named TrimbleMapViewFactory.swift in the same directory as your TrimbleMapView.swift:
ios/Runner/TrimbleMapViewFactory.swift
Step 2: Add the TrimbleMapViewFactory Class Code
Paste the following Swift code into the TrimbleMapViewFactory.swift file.
import Foundation
import Flutter
/**
* Factory responsible for creating instances of TrimbleMapView.
* Extends FlutterPlatformViewFactory to integrate with Flutter's
* platform view mechanism using StandardMessageCodec.
*/
class TrimbleMapViewFactory: NSObject, FlutterPlatformViewFactory {
private var messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
/**
* Called by Flutter when a new platform view is needed.
* @param frame The frame for the view
* @param viewId Unique identifier for this view instance
* @param args Initialization parameters passed from Dart
* @return A new instance of TrimbleMapView
*/
func create(
withFrame frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?
) -> FlutterPlatformView {
// Create and return the native TrimbleMapView with provided frame, view ID, arguments, and messenger
return TrimbleMapView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger
)
}
/**
* Returns the codec used to encode/decode arguments.
* StandardMessageCodec is the default mechanism Flutter uses.
*/
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
Step 3: Review the Code
- Class Definition:
TrimbleMapViewFactoryimplements Flutter’sFlutterPlatformViewFactoryprotocol.- It uses
FlutterStandardMessageCodec.sharedInstance()which is the default mechanism Flutter uses to encode theargsparameter passed during view creation. - The initializer takes a
FlutterBinaryMessengerinstance. This is essential because theTrimbleMapViewneeds it to set up itsMethodChannelfor communication back to Flutter.
createMethod:- This is the core method implemented from
FlutterPlatformViewFactory. Flutter calls this method automatically whenever your Dart code requests a new instance of the platform view associated with this factory. frame: CGRect: The frame for the view.viewId: Int64: A unique integer ID assigned by Flutter for this specific instance of the platform view.args: Any?: This parameter holds the data passed from your Dart code when you create theUiKitViewwidget. In this example, we expect it to be a dictionary containing the API key and region.- Instantiation: Finally, it creates a new
TrimbleMapViewinstance, passing the required parameters. This newTrimbleMapViewobject is then returned to Flutter, which handles embedding its view into the Flutter UI.
- This is the core method implemented from
createArgsCodecMethod:- Returns the codec used to encode/decode arguments.
FlutterStandardMessageCodecis the standard codec that handles common types like strings, numbers, lists, and maps.
- Returns the codec used to encode/decode arguments.
With the TrimbleMapView and its TrimbleMapViewFactory created, the next step is to register this factory with the Flutter engine so that Flutter knows how to create your native map view when requested from Dart.
Register the PlatformView Factory
Now that you have the native view (TrimbleMapView) and its factory (TrimbleMapViewFactory), you need to tell Flutter about the factory so it can use it. This registration happens in your app’s AppDelegate.swift.
Step 1: Locate and Modify AppDelegate.swift
Open your app delegate file, located at:
ios/Runner/AppDelegate.swift
Step 2: Add the Factory Registration Code
Update your AppDelegate.swift to include the factory registration:
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
if let pluginRegistrar = self.registrar(forPlugin: "TrimbleMapViewPlugin") {
let factory = TrimbleMapViewFactory(messenger: pluginRegistrar.messenger())
pluginRegistrar.register(factory, withId: "trimble_map_view")
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Step 3: Review the Code
registrar(forPlugin:): Obtains a registrar for the plugin, which provides access to the binary messenger.TrimbleMapViewFactory(messenger:): Creates an instance of the factory, passing the messenger forMethodChannelcommunication.pluginRegistrar.register(factory, withId:): Registers the factory with the view type identifier"trimble_map_view". You must use this exact same string in your Dart code when creating theUiKitViewwidget.
With the factory registered, the iOS native side is now fully configured. The next steps involve writing the Dart code in your Flutter application to display this native view and communicate with it.
Implement the Flutter UI (main.dart)
With the native iOS components set up and registered, we can now write the Flutter code to display the map and add interactive controls.
This involves:
- Setting up the main application structure.
- Using the
UiKitViewwidget to embed the native view, identified by theviewTypestring we registered earlier. - Passing the API key and region to the native side during view creation.
- Establishing a
MethodChannelto communicate between Dart and the nativeTrimbleMapView. - Handling the
mapReadycallback from native code to know when the map is initialized. - Creating UI buttons that invoke methods (like
zoomIn,toggleStyle) on the native map view via theMethodChannel.
Step 1: Locate main.dart
This code typically resides in the main entry point of your Flutter application:
lib/main.dart
Step 2: Add the Flutter Application Code
Replace the contents of your lib/main.dart with the following code:
import 'package:flutter/foundation.dart'; // Provides defaultTargetPlatform
import 'package:flutter/material.dart'; // Material widgets and theming
import 'package:flutter/services.dart'; // MethodChannel and message codecs
void main() {
// Ensure Flutter framework is fully initialized before creating the app
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp()); // Launch the app
}
/// Root widget of the application
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
// IMPORTANT: Replace 'YOUR_API_KEY' with your actual Trimble Maps API key
home: MapWithControls(
apiKey: 'YOUR_API_KEY',
region: 'worldwide', // Options: 'worldwide', 'northAmerica', 'europe'
),
);
}
}
/// Stateful widget that hosts the native Trimble map and control buttons
class MapWithControls extends StatefulWidget {
final String apiKey; // API Key passed from MyApp
final String region; // Region for Trimble Maps services
const MapWithControls({
super.key,
required this.apiKey,
this.region = 'worldwide', // Options: 'worldwide', 'northAmerica', 'europe'
});
@override
State<MapWithControls> createState() => _MapWithControlsState();
}
class _MapWithControlsState extends State<MapWithControls> {
// Channel for communicating Dart -> Native (TrimbleMapView)
late MethodChannel _channel;
// Flag to track if the native map has finished loading its initial style
bool _mapReady = false;
/// Callback triggered by UiKitView widget AFTER the native view is created.
void _onPlatformViewCreated(int id) {
debugPrint('Native PlatformView created with id: $id');
// Initialize the MethodChannel.
// IMPORTANT: The channel name MUST match the one used in TrimbleMapView.swift
_channel = MethodChannel('trimble_map_view_$id');
// Set up a handler for messages FROM Native TO Dart.
_channel.setMethodCallHandler((call) async {
debugPrint('Dart received method call: ${call.method}');
// Check if the native side signaled that the map is ready
if (call.method == 'mapReady') {
debugPrint('mapReady received from native, enabling controls.');
// Update state to enable UI controls that interact with the map
setState(() {
_mapReady = true;
});
}
// Handle other potential methods from native if needed
});
}
@override
Widget build(BuildContext context) {
// This example uses UiKitView, so restrict to iOS platform
if (defaultTargetPlatform != TargetPlatform.iOS) {
return const Scaffold(
body: Center(child: Text('This example currently supports iOS only.')),
);
}
// Main UI structure
return Scaffold(
appBar: AppBar(title: const Text('Trimble Map Flutter Example')),
body: Stack( // Use Stack to overlay controls on top of the map
children: [
// The widget that embeds the native iOS view
UiKitView(
// IMPORTANT: Must match the viewType registered in AppDelegate.swift
viewType: 'trimble_map_view',
// Callback function called when the native view is created
onPlatformViewCreated: _onPlatformViewCreated,
// Parameters passed to the native PlatformViewFactory's 'create' method
creationParams: <String, dynamic>{
// IMPORTANT: Keys must match those expected in TrimbleMapView.swift
'apiKey': widget.apiKey,
'region': widget.region,
},
// Codec used for encoding 'creationParams', must match the factory
creationParamsCodec: const StandardMessageCodec(),
),
// Overlay container for control buttons
Positioned(
bottom: 20,
left: 20,
right: 20, // Allow buttons to space out if needed
child: Card( // Wrap buttons in a Card for better visibility
elevation: 4.0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap( // Use Wrap for button layout flexibility
spacing: 8.0, // Horizontal space between buttons
runSpacing: 4.0, // Vertical space if buttons wrap
alignment: WrapAlignment.center,
children: [
// --- Map Control Buttons ---
ElevatedButton(
// Enable button only when _mapReady is true
onPressed: _mapReady ? _zoomIn : null,
child: const Text('Zoom In'),
),
ElevatedButton(
onPressed: _mapReady ? _zoomOut : null,
child: const Text('Zoom Out'),
),
ElevatedButton(
onPressed: _mapReady ? _toggleStyle : null,
child: const Text('Toggle Style'),
),
ElevatedButton(
onPressed: _mapReady ? _centerMap : null,
child: const Text('Center (CA)'),
),
],
),
),
),
),
],
),
);
}
// --- Button Action Methods ---
void _zoomIn() {
debugPrint('Button: Zoom In');
// Invoke the 'zoomIn' method on the native side via the channel
_channel.invokeMethod('zoomIn');
}
void _zoomOut() {
debugPrint('Button: Zoom Out');
// Invoke the 'zoomOut' method on the native side
_channel.invokeMethod('zoomOut');
}
void _toggleStyle() {
debugPrint('Button: Toggle Style');
// Invoke the 'toggleStyle' method on the native side
_channel.invokeMethod('toggleStyle');
}
void _centerMap() {
// Define coordinates (Example: somewhere in California)
const double lat = 36.7783;
const double lng = -119.4179;
debugPrint('Button: Center Map ($lat, $lng)');
// Invoke the 'center' method on the native side, passing arguments
_channel.invokeMethod('center', <String, double>{
'lat': lat,
'lng': lng,
});
}
}
Summary and Next Steps
Congratulations! You have successfully integrated a basic Trimble Map view into a Flutter application for iOS using PlatformView.
In this guide, we covered the essential steps:
- Configured Dependencies: Added the Trimble Maps SDK frameworks using Carthage and embedded them in the Xcode project.
- Configured Permissions: Added location permissions to
Info.plistfor location services support. - Implemented Native View: Created the
TrimbleMapView.swiftclass, which implementsFlutterPlatformView, initializes the native TrimbleTMGLMapView, handles account initialization viaAccountManagerDelegate, and sets up aMethodChannelfor communication. - Created View Factory: Implemented
TrimbleMapViewFactory.swiftto enable Flutter to create instances of the nativeTrimbleMapView, passing the API key and region during creation. - Registered Factory: Updated
AppDelegate.swiftto register theTrimbleMapViewFactorywith the Flutter engine using a unique view type identifier ("trimble_map_view"). - Built Flutter UI: Developed the Dart code in
lib/main.dartusing theUiKitViewwidget to embed the native map. - Established Communication: Set up the
MethodChannelon both the Dart and native sides to:- Send commands (zoom, style change, center) from Flutter buttons to the native map.
- Receive a
mapReadysignal from the native map to enable Flutter UI controls.
Get Support
If you encounter issues specifically related to the Trimble Maps SDK, this integration guide, or have further questions about using our SDK features, please don’t hesitate to reach out to our support team:
- Email: support@trimblemaps.com
Further Reading
This guide focused on the fundamental setup for displaying a map using Flutter’s PlatformView on iOS. The Trimble Maps SDK for iOS offers a wide range of additional features not covered here, such as:
- Displaying user location
- Routing
- Advanced camera controls
- Custom map styling
- Displaying custom data
To explore these features and gain a deeper understanding of the underlying native SDK capabilities, please refer to the comprehensive Trimble Maps SDK for iOS - Getting Started Guide.
We encourage you to use the foundation built in this guide as a starting point for integrating more advanced map functionalities into your Flutter applications.