Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to open flutter carplay with proper data without opening app? #56

Open
richanshah opened this issue Oct 17, 2024 · 6 comments
Open

Comments

@richanshah
Copy link

Steps to reproduce

Open app in carplay and mobile device both
Play any song from carplay
Kill the app from mobile and observe that carplay app also stopped
Now open the app in carplay only
Observe
You will see "Please wait while data fetching" on collections, playlists and songs screen when you kill the app and then open app in carplay
Also this happens when you close the app and go back into the app in carplay only then click music you just get spinning.

[✓] Flutter (Channel stable, 3.19.5, on macOS 14.2.1 23C71 darwin-arm64 (Rosetta), locale en-IN)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.1)

@viniciusoliverrs
Copy link

Hi, @richanshah. I managed to find the solution to this issue with the comment at this link #12 (comment) -1150229720

@richanshah
Copy link
Author

OK, Thanks I will give it a try.

@derrik-fleming
Copy link

@richanshah did you have any luck with the solution in the link provided? I was not able to get it working.

@viniciusoliverrs
Copy link

@derrik-fleming
"I have found a way to launch the Flutter app when starting the app through the CarPlay interface when the app is not running on the foreground. The solution is based on this blog post: https://adapptor.com.au/blog/enhance-existing-apps-with-carplay where they solve it for ReactNative.

The main idea is to move the FlutterEngine creation to the AppDelegate, which will be run when the app is launched from anywhere (CarPlay or device). We can run the FlutterEngine in headless mode, so the dart code can run before the engine is attached to any actual view (which happens when launching the app first on the CarPlay). Then, on the SceneDelegate we reuse the engine and attach it to the actual view.

So the AppDelegate.swift will be like this:

import UIKit
import Flutter

let flutterEngine = FlutterEngine(name: "SharedEngine", project: nil, allowHeadlessExecution: true)

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application( _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

flutterEngine.run()
GeneratedPluginRegistrant.register(with: flutterEngine)

return super.application(application, didFinishLaunchingWithOptions: launchOptions);

}
}
and the SceneDelegate.swift like this:

@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 controller = FlutterViewController.init(engine: flutterEngine, nibName: nil, bundle: nil)
    window?.rootViewController = controller
    window?.makeKeyAndVisible()
}

}
With this we have the dart code running when the user launches the app from the CarPlay interface. However, some changes are needed on this plugin as well, as it will fail to update the rootInterface if it was null when launching the CarPlayScene. The solution here is to modify the FlutterCarplayPluginSceneDelegate so it doesn't set the interfaceController to nil if the rootTemplate is not set at startup. You have to replace the templateApplicationScene with the following snippet:

func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController) {
FlutterCarPlaySceneDelegate.interfaceController = interfaceController

SwiftFlutterCarplayPlugin.onCarplayConnectionChange(status: FCPConnectionTypes.connected)
let rootTemplate = SwiftFlutterCarplayPlugin.rootTemplate

if rootTemplate != nil {
  FlutterCarPlaySceneDelegate.interfaceController?.setRootTemplate(rootTemplate!, animated: SwiftFlutterCarplayPlugin.animated, completion: nil)
}

}
Then on your dart code, after setting the root template using FlutterCarplay.setRootTemplate you have to force the plugin to update the displayed template using: _flutterCarplay.forceUpdateRootTemplate();"

@richanshah
Copy link
Author

I tried above code but it is not working for me.

@derrik-fleming
Copy link

derrik-fleming commented Nov 5, 2024

@viniciusoliverrs I was able to get it working. But I have a question about something that doesn't seem to be working in the same way as it does when the app is launched from the mobile device.

Do you know how I could get an accurate value for FlutterCarplay.connectionStatus when launching the app from the CarPlay device? It is initialized with CPConnectionStatusTypes.unknown, and it doesn't seem like the connection changed events stream is getting initialized until after the event is generated. This results in an inaccurate status until another connection changed event occurs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants