In the application I made with Flutter, I integrated CarPlay with the flutter_carplay package. When an item is clicked on the POI screen in Carplay, it needs to open the map in Carplay and navigate to the location of that item.
To reproduce create a flutter app and add the flutter_carplay plugin replace the FCPPointOfInterest.swift code in that and use a POI template and try to navigate from carplay.
I have attached the code of FCPPointOfInterest. I have changed the plugin code
//
// FCPPointOfInterest.swift
// Runner
//
// Created by Olaf Schneider on 15.02.22.
//
import CarPlay
@available(iOS 14.0, *)
class FCPPointOfInterest {
private(set) var _super: CPPointOfInterest?
private(set) var elementId: String
private var latitude: Double
private var longitude: Double
private var title: String
private var subtitle: String?
private var summary: String?
private var detailTitle: String?
private var detailSubtitle: String?
private var detailSummary: String?
private var image: String?
private var primaryButton: CPTextButton?
private var objcPrimaryButton: FCPTextButton?
private var secondaryButton: CPTextButton?
private var objcSecondaryButton: FCPTextButton?
static let maxPinImageSize: CGFloat = 40
init(obj: [String: Any]) {
self.elementId = obj["_elementId"] as! String
let lat = obj["latitude"] as! NSNumber
self.latitude = lat.doubleValue
let lng = obj["longitude"] as! NSNumber
self.longitude = lng.doubleValue
self.title = obj["title"] as! String
self.subtitle = obj["subtitle"] as? String
self.summary = obj["summary"] as? String
self.detailTitle = obj["detailTitle"] as? String
self.detailSubtitle = obj["detailSubtitle"] as? String
self.detailSummary = obj["detailSummary"] as? String
self.image = obj["image"] as? String
let primaryButtonData = obj["primaryButton"] as? [String: Any]
if primaryButtonData != nil {
self.objcPrimaryButton = FCPTextButton(obj: primaryButtonData!)
// Create a new CPTextButton with all required parameters
let button = CPTextButton(
title: self.objcPrimaryButton?.get.title ?? "",
textStyle: .normal, // You can change to .confirm if needed
handler: { [weak self] _ in
self?.launchGoogleMapsWithCarPlay()
}
)
self.primaryButton = button
}
let secondaryButtonData = obj["secondaryButton"] as? [String: Any]
if secondaryButtonData != nil {
self.objcSecondaryButton = FCPTextButton(obj: secondaryButtonData!)
self.secondaryButton = self.objcSecondaryButton?.get
}
}
func launchGoogleMapsWithCarPlaytry() {
// Create the Google Maps URL with the destination coordinates
let urlString = "comgooglemaps://?daddr=(latitude),(longitude)&directionsmode=driving"
// Check if Google Maps is installed
if let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) {
// Open Google Maps
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
// Fallback to Apple Maps if Google Maps isn't installed
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let placemark = MKPlacemark(coordinate: coordinate)
let mapItem = MKMapItem(placemark: placemark)
let launchOptions: [String: Any] = [
MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsShowsTrafficKey: true,
MKLaunchOptionsMapTypeKey: MKMapType.standard.rawValue,
]
mapItem.openInMaps(launchOptions: launchOptions)
}
}
func launchGoogleMapsWithCarPlay() {
// Check if app is running in CarPlay mode
guard let carPlayScene = UIApplication.shared.connectedScenes.first(where: { $0 is CPTemplateApplicationScene }) as? CPTemplateApplicationScene,
let interfaceController = carPlayScene.delegate as? CPTemplateApplicationSceneDelegate else {
// If CarPlay is not connected, fall back to mobile navigation
// launchMobileNavigation()
return
}
// Create the Google Maps URL specifically for CarPlay
let urlString = "comgooglemaps://?daddr=(latitude),(longitude)&directionsmode=driving"
if let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) {
// Open Google Maps
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
// Fallback to Apple Maps if Google Maps isn't installed
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let placemark = MKPlacemark(coordinate: coordinate)
let mapItem = MKMapItem(placemark: placemark)
let launchOptions: [String: Any] = [
MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsShowsTrafficKey: true,
MKLaunchOptionsMapTypeKey: MKMapType.standard.rawValue
]
mapItem.openInMaps(launchOptions: launchOptions)
}
}
// Fallback function for mobile navigation
private func launchMobileNavigation() {
let urlString = "comgooglemaps://?daddr=(latitude),(longitude)&directionsmode=driving"
if let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let placemark = MKPlacemark(coordinate: coordinate)
let mapItem = MKMapItem(placemark: placemark)
let launchOptions: [String: Any] = [
MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsShowsTrafficKey: true,
MKLaunchOptionsMapTypeKey: MKMapType.standard.rawValue
]
mapItem.openInMaps(launchOptions: launchOptions)
}
}
private func launchMapsApp() {
if UIApplication.shared.canOpenURL(URL(string: "comgooglemaps://")!) {
if let url = URL(
string: "comgooglemaps://?daddr=(latitude),(longitude)&directionsmode=driving")
{
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
} else {
if let url = URL(
string:
"https://www.google.com/maps/dir/?api=1&destination=(latitude),(longitude)&travelmode=driving"
) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
var get: CPPointOfInterest {
let location = MKMapItem(
placemark: MKPlacemark(
coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude)))
var pinImage: UIImage? = nil
if let image = self.image {
let key = SwiftFlutterCarplayPlugin.registrar?.lookupKey(forAsset: image)
pinImage = UIImage(named: key!)
if let pImage = pinImage {
if pImage.size.height > FCPPointOfInterest.maxPinImageSize
|| pImage.size.width > FCPPointOfInterest.maxPinImageSize
{
pinImage = pImage.resizeImageTo(
size: CGSize(
width: FCPPointOfInterest.maxPinImageSize,
height: FCPPointOfInterest.maxPinImageSize))
}
}
}
let poi = CPPointOfInterest(
location: location, title: title, subtitle: subtitle, summary: summary,
detailTitle: detailTitle, detailSubtitle: detailSubtitle,
detailSummary: detailSummary, pinImage: pinImage)
if let primaryButton = self.primaryButton {
poi.primaryButton = primaryButton
}
if let secondaryButton = self.secondaryButton {
poi.secondaryButton = secondaryButton
}
self._super = poi
return poi
}
}
3