Lewis Cianci I'm a passionate mobile-first developer, and I've been making apps with Flutter since it first released. I also use ASP.NET 5 for web. Given the chance, I'll talk to you for far too long about why I love Flutter so much.

Adding CarPlay to your Flutter app

7 min read 2145

Adding CarPlay To Your Flutter App

It wasn’t so long ago that the highest level of integration that you could expect between your phone and your car was playing music from your phone to your car speakers.

As time has gone on, this has changed drastically, with the introduction of technologies like Android Auto and Apple CarPlay. Now your chosen audio, fast-food, or navigation app can beam itself directly to your cars entertainment system for easy interactions.

While the intent between Apple CarPlay and Android Audio are similar, in this article we’ll look at how we can complement our Flutter app with some CarPlay functionality. We will cover:

Setting our expectations

Now, as the writer of this article, I would just love to jump into the exciting technical details of how to do this (as I am sure you would like to read about them!). However, before I do that, we need to briefly mention some of the things that could possibly obstruct you from ever getting your Flutter CarPlay app off the ground.

As developers, we’re always keen to find a way forward to deliver a certain product or feature. When we’re told that we can’t do something, or that something isn’t supported, we’re tempted to do it anyway just to see if we can be the first to do something, or because we really want to deliver for our customers.

However, in the case of developing an app that makes use of Apple CarPlay, we need to define some realistic expectations. This article relates to Flutter apps, but would apply to any app that leverages CarPlay. Let’s dive in.

Your CarPlay app requires Apple’s specific approval

Creating a Flutter app that has a CarPlay component is not as easy as just pulling a package in, setting up some values, and deploying it out to your users. Any app that uses CarPlay must specifically be approved by Apple.

This approval process can be long, sometimes to the order of months. There are also reports of Apple simply not getting back to developers who request this functionality. There’s nothing you can do or say to speed up this process — you are entirely on Apple’s timeline.

Even still, it’s not as simple as checking a box declaring that your app uses CarPlay. Your app must fall into a set of categories that Apple has pre-defined, which brings us to our next point.

You can’t run any type of app on CarPlay

To make use of CarPlay within your app, it has to fit into a certain set of categories that Apple prescribes. These categories are, essentially:

  • Audio app
  • Communication app (like Teams, Slack, or Discord)
  • “Driving Task” app (such as logging mileage in the user’s car — new in iOS 16)
  • EV charging app
  • Fueling app (to pay for fuel from CarPlay — new in iOS 16)
  • Navigation app (like Google Maps or Apple Maps)
  • Parking app (to pay for parking)
  • Quick food ordering app (like DoorDash or Uber Eats)

And that’s it.

Perhaps you feel that it’s draconian of Apple to enforce such restrictions on what you can and cannot develop for CarPlay. However, consider the premise of CarPlay: it’s something that people use in their car to complete basic tasks that should not distract them from driving.

Stock Photo Showing Infotainment System Displayed On Front Dashboard Of Car

If you developed an app that let people play Flappy Bird on their infotainment systems as they were driving, and they crashed their car, it’s true that the driver would be at fault for being so careless. But wouldn’t Apple bear some responsibility for allowing the app in the first place? In that light, the restrictions seem warranted.



So, considering the above, we’d only consider adding CarPlay to our Flutter app if:

If we ignore the above and just send apps out for Apple’s endorsement, they will likely not take the time to help your app become compliant. They will reject your application, or worse, just never get back to you, leaving your app stranded without an official approval (which you can use) or a rejection (which you can possibly escalate).

In case Apple does not approve our app, it’s important for us not to pin the success or failure of our app on having the CarPlay feature, especially considering that Apple may not tell us why the app was rejected.

With all of that out of the way, let’s dig in to how to actually add CarPlay to our Flutter app.

Setting up the basics of our CarPlay Flutter app

For today’s example, we’ll be bringing CarPlay functionality to our fast-food ordering app, which is called EatUp. On our phone, this is what the app looks like:

Phone Screen Showing Example Fast Food Ordering App Features In Action, From Order Screen To Pizza Options And Ordering Feature

The focus of this article isn’t on Flutter app development itself, but rather on how to add CarPlay to the app. You are welcome to peruse the source code to understand how we achieved the layout shown above, but we won’t go into further detail here.

Adding CarPlay to our Flutter project

The configuration steps for flutter_carplay are available on pub.dev, so let’s go through them now.

Note that in doing this, you will be making changes to your Flutter project. If you are adding CarPlay to an existing app, now would be an ideal time to commit your changes to source control. This way, you can easily revert your changes if they cause problems.

First of all, we want to add a reference to our pubspec.yaml in our dependencies:

dependencies:
  flutter:
    sdk: flutter
  ...other dependencies...
  flutter_carplay: ^1.0.3

Then, we should run the flutter pub get command within our project. This will create a Podfile that helps integrate the native parts of flutter_carplay with our project.

Let’s open the ios/Podfile file and uncomment the top line, and set the global platform to iOS 14.0:

# Uncomment this line to define a global platform for your project
platform :ios, '14.0'

Change directories into the ios folder and run pod install --repo-update.

Modifying our XCode project

Because CarPlay is a native iOS feature, we will need to make changes to our XCode project. Right-click on the ios folder, and then choose Flutter > Open iOS module in Xcode:

Demonstration Of Steps To Modify Xcode Project Showing Submenus After Right Clicking Ios Folder And Hovering Over Flutter Menu Item

Within our project, navigate to Runner > Runner > AppDelegate and replace the contents of our application function with a simple return true. In my case, I commented out the old code and added the new code directly beneath it, so it should look the same for you:

Appdelegate Folder Contents With Edited Application Function

Within our Runner folder (not the topmost one, but the one that exists one level down), create a SceneDelegate.swift file, and set the contents to the below:

@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = scene as? UIWindowScene else { return }

        window = UIWindow(windowScene: windowScene)

        let flutterEngine = FlutterEngine(name: "SceneDelegateEngine")
        flutterEngine.run()
        GeneratedPluginRegistrant.register(with: flutterEngine)
        let controller = FlutterViewController.init(engine: flutterEngine, nibName: nil, bundle: nil)
        window?.rootViewController = controller
        window?.makeKeyAndVisible()
    }
}

After you have done this, the SceneDelegate should look like this:

Scenedelegate File Contents

Now, right-click on the Info.plist file and click on Open As > Source Code. This will open the Info.plist in plain text so that you can easily paste the next configuration into the file.


More great articles from LogRocket:


We should be extra careful during this step because it’s very easy to accidentally break this file if we type the wrong thing into it:

Steps Within Runner File Showing How To Right Click Info File, Navigate To Open As Menu Option, And Select Correct Source Code File That Should Be Opened

Before the closing </dict>, add the following text. This is the default configuration, and we will come back to this later to customize it:

<key>UIApplicationSceneManifest</key>
<dict>
  <key>UIApplicationSupportsMultipleScenes</key>
  <true />
  <key>UISceneConfigurations</key>
  <dict>
    <key>CPTemplateApplicationSceneSessionRoleApplication</key>
    <array>
      <dict>
        <key>UISceneConfigurationName</key>
        <string>CarPlay Configuration</string>
        <key>UISceneDelegateClassName</key>
        <string>flutter_carplay.FlutterCarPlaySceneDelegate</string>
      </dict>
    </array>
    <key>UIWindowSceneSessionRoleApplication</key>
    <array>
      <dict>
        <key>UISceneConfigurationName</key>
        <string>Default Configuration</string>
        <key>UISceneDelegateClassName</key>
        <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
        <key>UISceneStoryboardFile</key>
        <string>Main</string>
      </dict>
    </array>
  </dict>
</dict>

Setting up our entitlements

Even in development, we need to permit our app to interact with the CarPlay simulator on our computer. Let’s configure these entitlements now.

Within XCode, click on Runner, then click on Signing & Certificates, and then click on the + button to the left of All:

Opening List Of All Signing And Certificates Options To Configure Entitlements

In the list that comes up, select Keychain Sharing. Your app doesn’t actually use keychain sharing, but doing this sets you up with a Runner.entitlements file in your project:

Red Arrow Pointing Left To Runner Entitlements File Resulting From Previous Step

Within this file, remove the Keychain Access Groups and add a new entitlement, depending on what type of app you are developing. You can check Apple’s list of available entitlements to see which one to use for your project.

When you are done, your Runner.entitlements file should look like the following:

Runner Entitlements File Contents With Keychain Access Groups Removed And New Entitlement Added

Now we’re finally ready to add some functionality to our Flutter app! 🎉

Calling CarPlay functionality from our Flutter app

Now, we’ll add the following UI into the CarPlay component of our app.

Gif Showing Carplay Ui For Pizza Ordering App Including Pizza Selection Options, Order Confirmation Options, And Order Confirmation Dismissal

Here’s we will need to do in order to achieve this:

  1. Set a RootTemplate that will form the basis of our CarPlay experience
  2. Add details to the template
  3. Set up callbacks within our app

Let’s go ahead and take care of these steps now.

Setting a RootTemplate

Within our main.dart file, we’ll create a new FlutterCarplay object. This object will be responsible for handling our connection to CarPlay, and will let us control what is happening within our CarPlay experience:

final FlutterCarplay _flutterCarplay = FlutterCarplay();

Next, within our initState for our widget, we want to set up the RootTemplate with some options to tap on. It’s fairly easy to create a grid template, set a title, and then describe some buttons that we would like to be able to be pressed:

@override
void initState() {
  FlutterCarplay.setRootTemplate(
    rootTemplate: CPGridTemplate(
      title: "What pizza?",
      buttons: [
        ...Data.FoodItems.map(
          (e) => CPGridButton(
            titleVariants: [e.name],
            image: 'assets/images/${e.asset}',
            onPress: () {
              showOrderingConfirmationSheet(e);
              // showActionSheet();
            },
          ),
        )
      ],
    ),
    animated: true,
  );

  _flutterCarplay.forceUpdateRootTemplate(); // This makes the CarPlay experience reload on hot reload, useful during development.

  // TODO: implement initState
  super.initState();
}

As expected, this is the result:

Six Types Of Pizza Shown In Pizza Ordering Options Displayed Within Carplay Flutter App Ui

Now, let’s implement showOrderingConfirmationSheet:

void showOrderingConfirmationSheet(FoodItem food) {
  FlutterCarplay.showActionSheet(
    template: CPActionSheetTemplate(
      title: "Order ${food.name}?",
      message: "Your pizza will be ready soon after confirmation.",
      actions: [
        CPAlertAction(
          title: "Cancel",
          style: CPAlertActionStyles.cancel,
          onPress: () {
            // print("Cancel pressed in action sheet");
            FlutterCarplay.popModal(animated: true);
          },
        ),
        CPAlertAction(
          title: "Ok",
          style: CPAlertActionStyles.normal,
          onPress: () {
            print("Ok pressed in action sheet");
            FlutterCarplay.popModal(animated: true);
            FlutterCarplay.showAlert(
                template: CPAlertTemplate(titleVariants: [
              '${food.name} ordered!'
            ], actions: [
              CPAlertAction(
                title: "Dismiss",
                style: CPAlertActionStyles.destructive,
                onPress: () {
                  print("Dismiss pressed in action sheet");
                  FlutterCarplay.popModal(animated: true);
                },
              ),
            ]));
          },
        ),
      ],
    ),
  );
}

This results in the particular food item being opened and a confirmation message being displayed. You can also call native functionality within your Flutter app at this stage, such as adding or removing items from an internal database, depending on your needs.

Conclusion

As always, you can retrieve a full copy of the source code on Github. There is a commit before I implemented CarPlay, and a commit after I had implemented it, so you can use this to compare what changed. This comparison can be useful in case you experience difficulties in implementing CarPlay.

So, that’s how you add CarPlay to your Flutter app. The most time-consuming component of adding CarPlay is getting Apple to approve your use of CarPlay within your app. If you want to start using CarPlay within your Flutter app, that’s something you should start working on as soon as you can.

Adding CarPlay to your Flutter app can be quite a process, but if CarPlay is a good fit for your app, it can add a lot of value to your app’s experience.

: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

.
Lewis Cianci I'm a passionate mobile-first developer, and I've been making apps with Flutter since it first released. I also use ASP.NET 5 for web. Given the chance, I'll talk to you for far too long about why I love Flutter so much.

Leave a Reply