oguzhnatly / flutter_carplay

🚗 Apple CarPlay for Flutter Apps. Aims to make it safe to use apps made with Flutter in the car by integrating with CarPlay.
https://pub.dev/packages/flutter_carplay
MIT License
217 stars 63 forks source link

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

Open richanshah opened 1 month ago

richanshah commented 1 month ago

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 commented 1 month ago

Hi, @richanshah. I managed to find the solution to this issue with the comment at this link https://github.com/oguzhnatly/flutter_carplay/issues/12#issuecomment -1150229720

richanshah commented 1 month ago

OK, Thanks I will give it a try.

derrik-fleming commented 4 weeks ago

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

viniciusoliverrs commented 4 weeks ago

@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 commented 4 weeks ago

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

derrik-fleming commented 3 weeks ago

@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.

richanshah commented 2 weeks ago

I'm trying to use FlutterCarplay.connectionStatus to retrieve the CarPlay connection status for fetching data when CarPlay opens without the app running. However, the status is not being returned consistently, which is causing some issues.

If you know of any workarounds or have suggestions on how to get a reliable connection status in this scenario, please let me know.

Thank you!

harpreetslabs commented 2 weeks ago

@richanshah did you manage to solve the issue when Carplay opens without opening app? i tried above solution but does not work, it shows blank screen when opens directly from carPlay.

derrik-fleming commented 2 weeks ago

@richanshah did you manage to solve the issue when Carplay opens without opening app? i tried above solution but does not work, it shows blank screen when opens directly from carPlay.

@harpreetslabs Ensure the app is not still running on CarPlay in the background. Try uninstalling the app first before testing the above code if you have not already. Or open it on the mobile device first, then close (which will close the app on CarPlay), and then try opening it on the CarPlay device. It seems like if the app is running on CarPlay in the background you may continue to have a blank screen.

harpreetslabs commented 2 weeks ago

@richanshah did you manage to solve the issue when Carplay opens without opening app? i tried above solution but does not work, it shows blank screen when opens directly from carPlay.

@harpreetslabs Ensure the app is not still running on CarPlay in the background. Try uninstalling the app first before testing the above code if you have not already. Or open it on the mobile device first, then close (which will close the app on CarPlay), and then try opening it on the CarPlay device. It seems like if the app is running on CarPlay in the background you may continue to have a blank screen.

Yes, my question is how I can work by opening the app from carplay directly, instead of opening the flutter app first.

harpreetslabs commented 2 weeks ago

@derrik-fleming I noticed one thing, when we open directly from carplay it shows black screen, then on closing carplay and opening again works fine. (I have not opened the Flutter app yet).

richanshah commented 2 weeks ago

no, I have still the same issue as yours.

@derrik-fleming I noticed one thing, when we open directly from carplay it shows black screen, then on closing carplay and opening again works fine. (I have not opened the Flutter app yet).

harpreetslabs commented 2 weeks ago

no, I have still the same issue as yours.

@derrik-fleming I noticed one thing, when we open directly from carplay it shows black screen, then on closing carplay and opening again works fine. (I have not opened the Flutter app yet).

Any workaround for this?

derrik-fleming commented 2 weeks ago

@derrik-fleming I noticed one thing, when we open directly from carplay it shows black screen, then on closing carplay and opening again works fine. (I have not opened the Flutter app yet).

@harpreetslabs Are you using the connection events to determine if you should push templates on to CarPlay? The thing that I noticed with the solution in this thread is that when launching the app from CarPlay (without opening the mobile app) is that the connection status is never updated, so it remains in its initialized value (unknown) until another event occurs. The fact that it works for you on the second launch makes me think this might be part of your issue.

richanshah commented 2 weeks ago

I also facing same thing. I am trying different ways but nothing works yet

On Thu, Nov 7, 2024, 7:22 PM derrik-fleming @.***> wrote:

@derrik-fleming https://github.com/derrik-fleming I noticed one thing, when we open directly from carplay it shows black screen, then on closing carplay and opening again works fine. (I have not opened the Flutter app yet).

Are you using the connection events to determine if you should push templates on to CarPlay? The thing that I noticed with the solution in this thread is that when launching the app from CarPlay (without opening the mobile app) is that the connection status is never updated, so it remains in its initialized value (unknown) until another event occurs.

— Reply to this email directly, view it on GitHub https://github.com/oguzhnatly/flutter_carplay/issues/56#issuecomment-2462293652, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEA54E535H6IU2ICVYTRXJ3Z7NWBHAVCNFSM6AAAAABQDOOD5WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINRSGI4TGNRVGI . You are receiving this because you were mentioned.Message ID: @.***>

harpreetslabs commented 2 weeks ago

@richanshah @derrik-fleming you can try this fork: url: https://github.com/icapps/flutter_carplay.git # Where to find Repo ref: combine-all # branch name It works fine for me, it also have now playing, image from URL feature. I have only one issue in this branch when the app is opened and the carplay is running, killing the app also kills the carplay.

derrik-fleming commented 2 weeks ago

@richanshah @derrik-fleming you can try this fork: url: https://github.com/icapps/flutter_carplay.git # Where to find Repo ref: combine-all # branch name It works fine for me, it also have now playing, image from URL feature. I have only one issue in this branch when the app is opened and the carplay is running, killing the app also kills the carplay.

That's neat. I think all of the apps included with iOS behave in that same manner. So, I'm not sure that I would consider that an issue, unless you want it to perform otherwise.

richanshah commented 1 week ago

thanks but I don't want to kill carplay on killing the app.

richanshah commented 1 week ago

apps included with iOS behave in that same manner. So, I'm not sure that I would consider that an issue, unless you want it to perform otherwise.

if you see Spotify or Youtube music it does not work that day.

richanshah commented 1 week ago

https://stackoverflow.com/a/78546045/9248248

this works for me but the issue is I have lots of data for carplay so it takes more time in the app so it cause performance issue.

Ella-Kim913 commented 1 week ago

I have a same issue, thanks @viniciusoliverrs this works on debug/release mode, but crash the app on testflight build. Any suggestions?

viniciusoliverrs commented 1 week ago

Eu tenho o mesmo problema, obrigado@viniciusoliverrsisso funciona no modo debug/release, mas trava o aplicativo na compilação testflight. Alguma sugestão?

Can you explain better?

popeyelau commented 1 week ago

https://stackoverflow.com/a/78546045/9248248

this works for me but the issue is I have lots of data for carplay so it takes more time in the app so it cause performance issue.

//Firstly you can set an emtpy template for init
FlutterCarplay.setRootTemplate(
      rootTemplate: CPTabBarTemplate(
        templates: [
         TabPage1().get(),
         TabPage2().get(),
         TabPage3().get(),
        ],
      ),
      animated: true,
    );
carplay.forceUpdateRootTemplate();

// TabPage singleton
class  TabPage1 {
 static final TabPage1 _singleton = TabPage1._internal();
 CPListTemplate? template;
 List<CPListSection> sections = [];

  factory TabPage1() {
    return _singleton;
  }

  CPListTemplate get() {
    final template = CPListTemplate(
      sections: sections,
      title: "Title",
      emptyViewTitleVariants: [],
      systemIcon: "icon"
    );
    this.template = template;
    return template;
  }

  //call it when templateWillAppear 
  Future<void> refresh() async {
   if (sections.isNotEmpty) return;

   // fetch api data and setup list sections
   template?.udpate(sections);
  }
}

e.g: FlutterCarPlaySceneDelegate templateWillAppear https://github.com/popeyelau/flutter_carplay/blob/4265e31d5108f322e2cba497d41e09c5ea4d6b07/ios/Classes/FlutterCarplayPluginSceneDelegate.swift#L87

FCPListTemplate update https://github.com/popeyelau/flutter_carplay/blob/4265e31d5108f322e2cba497d41e09c5ea4d6b07/ios/Classes/models/list/FCPListTemplate.swift#L79

Ella-Kim913 commented 6 days ago

Eu tenho o mesmo problema, obrigado@viniciusoliverrsisso funciona no modo debug/release, mas trava o aplicativo na compilação testflight. Alguma sugestão?

Can you explain better?

I followed the necessary steps, and everything works fine in both debug and release modes. However, the app crashes only when building on TestFlight. The crash report points to CarPlay.

Updating CarPlay was the only change since the last successful TestFlight build.

Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000 Exception Codes: 0x0000000000000001, 0x0000000000000000

static SwiftFlutterCarplayPlugin.register(with:) + 120 (SwiftFlutterCarplayPlugin.swift:31)

Has anyone experienced a similar issue or have any ideas about what could be causing this?

harpreetslabs commented 4 days ago

I'm trying to use FlutterCarplay.connectionStatus to retrieve the CarPlay connection status for fetching data when CarPlay opens without the app running. However, the status is not being returned consistently, which is causing some issues.

If you know of any workarounds or have suggestions on how to get a reliable connection status in this scenario, please let me know.

Thank you!

Have you solved this issue? I could not get the correct status if CarPlay was already open and then the app was opened.

harpreetslabs commented 4 days ago

@derrik-fleming @richanshah

I tried to check using native code and added method channels. It works for me, but can you please check if it works fine?

Here is the implementation:


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)

    let methodChannel = FlutterMethodChannel(
      name: "carplay_status_channel",
      binaryMessenger: flutterEngine.binaryMessenger 
    )

    methodChannel.setMethodCallHandler { (call, result) in
      if call.method == "isCarPlayConnected" {
        result(self.isCarPlayConnected())
      } else {
        result(FlutterMethodNotImplemented)
      }
    }

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

  private func isCarPlayConnected() -> Bool {
    for screen in UIScreen.screens {
      if screen.traitCollection.userInterfaceIdiom == .carPlay {
        return true
      }
    }
    return false
  }
}
richanshah commented 4 days ago

ok thanks @harpreetslabs will update u

harpreetslabs commented 1 day ago

@derrik-fleming @richanshah Does your app open correctly? I am experiencing an issue where, when I open the app, it first shows the previous state, then displays the splash screen, and finally starts the app. Issue: https://github.com/oguzhnatly/flutter_carplay/issues/58

https://github.com/user-attachments/assets/35ee0286-3e65-4282-8437-ca831b30de9b