FlutterでCarPlayのサンプルを作ってみた

# はじめに
FlutterでCarPlayを作る機会があったので、サンプルを作ってみました。

# 更新履歴
2025.1.10 初回投稿

## 準備(ここはChatGPTに聞いてそのままコピペ)
1. Apple Developer Programへの登録
    まず、Apple Developer Programに登録している必要があります。まだ登録していない場合は、Apple Developerの公式サイトから登録してください。

2. CarPlay対応アプリのカテゴリ確認
Appleが現在CarPlay対応アプリとして認めているカテゴリは以下の通りです：
    - オーディオ
    - ナビゲーション
    - 通信（VoIPなど）
    - EV充電管理
    - 駐車
    - クイックフード注文
    - 燃料補給
注意: CarPlayアプリはこれらのカテゴリのいずれかに該当する必要があります。

3. 適合性確認

    アプリがCarPlay対応要件に準拠していることを確認する必要があります。Appleは、CarPlayアプリのUI、ユーザーエクスペリエンス、機能がガイドラインに従っていることを求めています。

    Human Interface Guidelines (HIG) for CarPlayを確認してください。
    
4. CarPlay App Entitlementの申請
    CarPlayアプリを開発するには、CarPlayの権限（Entitlement）をAppleにリクエストする必要があります。このプロセスでは、以下の情報を提供する必要があります：

    - アプリの詳細
    - 使用ケース
    - CarPlayとの統合計画
    - 申請は、Apple Developer Programの[お問い合わせフォーム](https://developer.apple.com/contact/topic/select)を通じて行います。)
        - 以下の手順で進めます：
         Apple Developer Programのアカウントにログイン。
        「お問い合わせ」セクションに移動。
        「技術サポート」または「APIの利用リクエスト」を選択。
        CarPlay App Entitlementのリクエストを具体的に記載し、送信します。


:::note info
ここでは、iOSシミュレータを使って実機は使わず「CarPlay App Entitlementの申請」の申請を行わない方法で試しています。
:::

## Flutterプロジェクト作成

1. 適当なプロジェクト名で作成

    ``` command
    flutter create carplay_sample
    ```

2. パッケージをインストール

    ``` command
    flutter pub add flutter_carplay
    ```

3. flutter_carplay([https://pub.dev/packages/flutter_carplay/example](https://pub.dev/packages/flutter_carplay/example))のサンプルコードをmain.dartにそのまま貼り付ける

4. そのまま貼り付けると、「images/logo_flutter_1080px_clr.png」がないと怒られるので、適当にアイコンを作って、入れておきます。その際、pubspec.yamlのassetsの設定を忘れずに...
プロジェクトの最上層に「assets」「images」ディレクトリを作成
以下のcarplay.pngを追加

```yaml
  assets:
    - assets/images/

```
![carplay.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/66e1c76c-99ff-1d07-e27c-d04ef4668718.png)

## XCodeの設定

### AppDelegateの修正
1. FlutterプロジェクトのiOS>「Open in XCode」からXCodeを開く

2. Runner>Runner内のAppDelegateを開く

3. 10,11行目の以下のコードをコメントアウトもしくは削除して、「return true」のみにして保存
```swift
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
//    GeneratedPluginRegistrant.register(with: self)
//    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
      return true
  }
}

```

### SceneDelegate.swift作成

1. 同じ階層のRunner内に、「SceneDelegate.swift」を作成して、以下のコードをそのままコピペ

```swift
@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()
    }
}
```
### info.plistに追加

5. 同じRunnerの階層内のinfo.plistに以下を付け加える

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

 <略>
 
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>CADisableMinimumFrameDurationOnPhone</key>
	<true/>
	<key>UIApplicationSupportsIndirectInputEvents</key>
	<true/>
  //ここから  
    <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>
    <key>NSSupportsCarPlay</key>
    <true/>
    //ここまで
</dict>
</plist>
```
### Runnerファイルの追加
1. プロジェクトの一番上の「Runner」をクリック
1. TARGETS>Runnerをクリック(最初から選択されている場合もあります)
1. 「Signing&Capabilities」タブをクリックします。
1. 「Signing&Capabilities」タブの下の「All」横の「＋Capability」をクリックするとポップアップが表示されます。
1. 一覧の中の「Keychain Sharing」をダブルクリックします。すると、Runner内にさらにRunnerファイルが作成されます。
![スクリーンショット 2025-01-10 18.14.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/496d2957-20ed-91ad-7ca4-93decc299958.png)
1. この「Runner」ファイルの「Entitlements File」の下にKeyが「Keychain Access Groups」となっているので、ここを「com.apple.developer.carplay-maps」に変更。typeも「Boolean」、Valueを「YES」に変更する
![スクリーンショット 2025-01-10 18.22.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/aa58077c-864c-68cf-dc4f-4ea2d2ea2fef.png)

## シミュレータで実行

1. 「flutter run」で実行
1.  iOSシミュレータにアプリの画面が表示されます。
![Simulator Screenshot - iPhone 16 Pro - 2025-01-10 at 18.08.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/a369658e-0741-2462-da85-694614a24184.png)



2. Simulatorメニューの「I/O」>「External Display」>「CarPlay...」を選択する
![スクリーンショット 2025-01-10 18.10.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/d6401d52-6b02-2b13-4e69-b515ba809c9d.png)

3. Setup画面が表示されるので、一旦このままで「run」ボタンを押す
![スクリーンショット 2025-01-10 18.11.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/99994dad-7955-4145-b729-c3c5aa614778.png)

4. CarPlay画面が表示されるので、該当のアプリをクリックすればアプリが表示されます。

![スクリーンショット 2025-01-10 18.13.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/3d0bed08-31e2-1ad0-4a11-cf32f078e8ab.png)