flutter / devtools

Performance tools for Flutter
https://flutter.dev/docs/development/tools/devtools/
BSD 3-Clause "New" or "Revised" License
1.53k stars 313 forks source link

[devtools_plugin_sdk] Detect web refresh on devtools plugin side #7779

Open davidmartos96 opened 1 month ago

davidmartos96 commented 1 month ago

Hello! I'm developing a devtools plugin which offers functionality to clear/remove a local sqlite file on web and io platforms.

The problem I'm having is that when the app does a window.location.reload() when a request to reset the database is performed, the devtools plugin doesn't have the chance to know about this event, so we cannot update UI to remove this database from a list for example. I tried using ServiceManager.connectedState but it doesn't seem to fit here, as the VM service session is the same one.

My implementation is as follows. It works correctly on IO platforms but not on web.

app side

void handleResetDbRequest() {
    // Delete database file / web indexeddb    
    await resetDb();

    // Let the plugin know that it needs to refresh the list of databases in the devtools UI
    postEvent('notify-db-update');

    // it does nothing on io and `window.location.reload()` on web
    await afterDbReset();
}

devtools plugin

Future<void> requestDbReset() async {
    // request a db reset to the app
    await serviceManager.callServiceExtensionOnMainIsolate(...);

    // Perform a hot restart of the app
    await serviceManager.performHotRestart();
}
kenzieschmoll commented 1 month ago

Hi @davidmartos96. A few clarifying questions to help us understand your problem:

when the app does a window.location.reload()

When you say "app" are you referring to the end user application that depends on your package (the package that provides the DevTools extension), or are you referring to your DevTools extension Flutter web app?

postEvent('notify-db-update')

Are you calling the postEvent method from dart:developer? https://api.dart.dev/stable/2.3.2/dart-developer/postEvent.html If so, these events come over the 'Extension' stream on the VM service. So you can listen for this event from your DevTools extension by calling serviceManager.service.onExtensionEvent.

I'm still not quite clear on how web refresh fits into your issue, so if you could clarify the user journey where you are hitting this problem, that may help me provide you with better advice. Thanks!

davidmartos96 commented 1 month ago

@kenzieschmoll When I mention app in the post I mean the user application side, because some parts of the plugin cannot be executed in the plugin-(app) side.

Yes, I'm doing the postEvent + onExtensionEvent. That part is working correctly on non web platforms. The problem on web is if I run window.location.reload() in the user application side.

For context, the extension lets you clear the state of a sqlite database in the browser. For that to work, the plugin tells the user-app to delete the database, and after that it does a hot restart. But on web, on top of a hot restart, a full window refresh is needed in order for the browser to delete the indexeddb.

But if the communication of the postEvent lags a bit, the plugin doesn't receive the event because of the refresh, so it doesn't get the chance to update the UI.

Hope this makes it a bit more clear.

kenzieschmoll commented 1 month ago

The problem on web is if I run window.location.reload() in the user application side.

Okay so this is the case where your end user is running a Flutter web app, and that web app is connected to your DevTools extension. Please correct me if I am wrong.

But if the communication of the postEvent lags a bit, the plugin doesn't receive the event because of the refresh

Can you try awaiting a short delay after you call postEvent? Another option is to implement a ping / pong like design, where you call postEvent from the end user app, and then wait for some response back from your DevTools extension before continuing. However to do the latter, you'd need to make sure you have a fallback timeout for the case where the end user app is not connected to your DevTools extension.

davidmartos96 commented 1 month ago

@kenzieschmoll Yes you are correct, it's when the user runs a web app.

Thank you for the tips! Yes, with 1 second delay before refreshing it works just fine. I opened the issue in case there was some kind of listener or event for this situation.

If there is no other method, I guess ping-pong would be the alternative to avoid sleeping longer than necessary.

kenzieschmoll commented 1 month ago

I opened the issue in case there was some kind of listener or event for this situation.

@bkonyi - is there any way to verify that your event was sent off by postEvent from dart:developer to avoid races like this?

bkonyi commented 1 month ago

I opened the issue in case there was some kind of listener or event for this situation.

@bkonyi - is there any way to verify that your event was sent off by postEvent from dart:developer to avoid races like this?

Nope. postEvent sends a message to the VM service isolate to post the event to a web socket, which means that the VM service isolate's event loop needs to pick up the message to process the message. Sending messages across isolates is an asynchronous operation that can't be awaited, so there's no way to determine if a message has been processed by the target isolate without an explicit bidirectional communication channel between isolates.

The right thing to do here is to wait for the notify-db-update event on the Extension stream in the DevTools extension before actually triggering the hot restart.

davidmartos96 commented 1 month ago

@bkonyi I see, thanks for the information!