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
218 stars 63 forks source link

FCPListItem completeHandler is null and won't run properly #17

Open bypass112 opened 2 years ago

bypass112 commented 2 years ago

There seems to be a problem with the loading wheel beeing infinite and list items not updating properly when list items are used in a push template through FlutterCarplay.push(). The main reason this appears seems to be that completeHandler becomes null immediately after the onPress event is fired and stopHandler from FCPListItem.swift won't finish properly. Is there any way to make sure that completeHandler won't become null until stopHandler is called? In order to reproduce you just have to put CPListItem(text: "Item 1", onPress: (complete, self) { print("BUTTON " + self.text); complete(); }), insted of CPListItem(text: "Item 1"}), on line 313 of the main.dart file in the example folder and click that item.

vanlooverenkoen commented 2 years ago

Ah alright. Yesterday I created a ticket with the full investigation: https://github.com/oguzhnatly/flutter_carplay/issues/23

I will close that issue and add the info here.

vanlooverenkoen commented 2 years ago

Add an onPress to the ListTemplate A Section

              CPListItem(
                text: "Item 1",
                onPress: (complete, self) {
                  print('Button Click');
                  complete();
                },
              ),

And you will see this behaviour.

https://user-images.githubusercontent.com/21172855/190155504-9cc52273-d5bf-46ad-b07e-d9cdcb02ac87.mov

I did some research. Turns out there is a major bug in the findItem method

  static func findItem(elementId: String, actionWhenFound: (_ item: FCPListItem) -> Void) {
    let objcRootTemplateType = String(describing: SwiftFlutterCarplayPlugin.objcRootTemplate).match(#"(.*flutter_carplay\.(.*)\))"#)[0][2]
    var templates: [FCPListTemplate] = []
    if (objcRootTemplateType.elementsEqual(String(describing: FCPListTemplate.self))) {
      templates.append(SwiftFlutterCarplayPlugin.objcRootTemplate as! FCPListTemplate)
      NSLog("FCP: FCPListTemplate")
    } else if (objcRootTemplateType.elementsEqual(String(describing: FCPTabBarTemplate.self))) {
      templates = (SwiftFlutterCarplayPlugin.objcRootTemplate as! FCPTabBarTemplate).getTemplates()
      NSLog("FCP: FCPTabBarTemplate")
    } else {
      NSLog("FCP: item not found 1: \(elementId)")
      return
    }
    for t in templates {
      for s in t.getSections() {
        for i in s.getItems() {
          NSLog("FCP: items: \(t.elementId) \(s.elementId) \(i.elementId)")
          if (i.elementId == elementId) {
            NSLog("FCP: item found: \(elementId)")
            actionWhenFound(i)
            return
          }
        }
      }
    }
    NSLog("FCP: item not found 2: \(elementId)")
  }

I added some logs. And these are the logs I got:

FCP: On Press Send event
FCP: SET ITEMS FOR: Optional("A Section"): items: 4
FCP: SET ITEMS FOR: Optional("B Section"): items: 2
FCP: SET ITEMS FOR: Optional("C Section"): items: 2
FCP: onListItemSelectedComplete: fb7982ac-12f4-4ddb-a315-69fcf9ffc789
FCP: FCPTabBarTemplate
FCP: GET ITEMS FOR: Optional("First Section"): items: 2
FCP: items: 300ef966-6cbb-4435-aebe-25e7dea0060f 51f17de1-ecc6-47f9-aed8-9a138ef19865 89283c2a-5e98-47eb-8f99-0c875098cfd6
FCP: items: 300ef966-6cbb-4435-aebe-25e7dea0060f 51f17de1-ecc6-47f9-aed8-9a138ef19865 6776af5d-4cb0-4572-a3cb-a4627ca29112
FCP: GET ITEMS FOR: Optional("Second Section"): items: 3
FCP: items: 300ef966-6cbb-4435-aebe-25e7dea0060f a6215f6c-5eb2-4b1a-8b0f-dc2739a8b188 0cb547b1-366e-47b8-bc5c-db6220bdeae4
FCP: items: 300ef966-6cbb-4435-aebe-25e7dea0060f a6215f6c-5eb2-4b1a-8b0f-dc2739a8b188 9759bd49-5366-45bf-b8a6-d72bf1dc44a1
FCP: items: 300ef966-6cbb-4435-aebe-25e7dea0060f a6215f6c-5eb2-4b1a-8b0f-dc2739a8b188 c577273f-520e-4816-ad18-bf6e7eb072f1
FCP: GET ITEMS FOR: Optional("Features"): items: 6
FCP: items: 6fb3dfc3-0112-418c-b5f7-90b7495d335f c7ee5afd-2770-4005-9a7e-7e22c30d33a4 084a1787-56e5-42d5-a2d8-02eafc0ce250
FCP: items: 6fb3dfc3-0112-418c-b5f7-90b7495d335f c7ee5afd-2770-4005-9a7e-7e22c30d33a4 153b464a-e856-46de-9f9d-f1dbd816381a
FCP: items: 6fb3dfc3-0112-418c-b5f7-90b7495d335f c7ee5afd-2770-4005-9a7e-7e22c30d33a4 eaf71ddf-d3af-4104-ac73-40314d257076
FCP: items: 6fb3dfc3-0112-418c-b5f7-90b7495d335f c7ee5afd-2770-4005-9a7e-7e22c30d33a4 fb7982ac-12f4-4ddb-a315-69fcf9ffc789
FCP: item found: fb7982ac-12f4-4ddb-a315-69fcf9ffc789
FCP: STOP HANDLER: fb7982ac-12f4-4ddb-a315-69fcf9ffc789

I also added a log to the onPress event that was sent and in the onListItemSelectedComplete, Also added logs to the init of the FCPListSection -> SET ITEMS FOR: ... And to the getItems of FCPListSection -> GET ITEMS FOR: ...

Turns out that the FCPTabBarTemplate is retrieved instead of the FCPListTemplate.

Workaround

Just don't select anything for some time. (couple of seconds +/-10 - 20) And you will be able to select another item. (The spinner will stay there as long as you don't call the complete method

bypass112 commented 2 years ago

I found another workaround for this bug: I'll just use a Queue backTree = Queue(); to remember the tree history and everytime I'll navigate to another screen I'll just use FlutterCarplay.setRootTemplate instead of FlutterCarplay.push then I'll put the last screen in backTree and when back is clicked I'll just pop the queue and populate the screen with FlutterCarplay.setRootTemplate from the queue. It isn't perfect because the previous screen won't have the same state but it works ok.

vanlooverenkoen commented 2 years ago

Is it possible to provide with this code? or create a draft pull request so we can also use it?

bypass112 commented 2 years ago

I'm sorry the code is a bit more complicated behind the scenes and I haven't modified the package at all, it is just in app, but I'll try my best to summarize it in order to help with the workaround, but again, the last page won't resume in its last position/state because you are overwriting the page everytime with FlutterCarplay.setRootTemplate.

  1. You will need a Queue backTree = Queue(); in which you will store the id's of the page history in order to simulate a navigation tree, but it isn't at all because there will always be one page.
  2. Set your rootTemplate with FlutterCarplay.setRootTemplate and display any elements you want.
  3. On the onPress of the child items add backTree.addLast(CPTreeInfo(pageId, pageTitle)) then call the function for the page construction that must have FlutterCarplay.setRootTemplate
  4. In the function for the construction of the page you will need to overwrite the backButton of the rootTemplate and call the same function that will take backTree.last.id and backTree.last.title and don't forget to call backTree.removeLast() to remove the last id from history
  5. In the special function that will construct the page from the backTree.last.id and backTree.last.title you will need to create the navigation tree by getting the stored children of backTree.last.id and then calling FlutterCarplay.setRootTemplate with the children of the parentId.
vanlooverenkoen commented 2 years ago

Hmm alright. Today I will be looking at the findItem method if I can fix it on the native side.

vanlooverenkoen commented 2 years ago

We fixed the issue in #26

vanlooverenkoen commented 2 years ago

26 also contains the now playing implementation