flutter_js
flutter_js copied to clipboard
Call a Dart function from JS and get the result back in JS context
Is there anyway to call a Dart function from JS and get the result back in JS context? E.g.:
flutterJs.evaluate("var a = await callDartFunction(); console.log(a);");
We are also trying to do this, but so far we haven't found a way. It is possible to do this in JavaScriptCore and QuickJS, but as we understand it, the only interface from JS to Dart is via sendMessage:
var upperCaseResult = sendMessage("dartToUpperCase", "hello");
Then in dart, you would have a listener:
flutterJs.onMessage('dartToUpperCase', (dynamic args) {
var arg = (args as string);
return arg.toUpperCase();
});
Unfortunately upperCaseResult
is undefined in javascript.
We also wondered whether this could be achieved using promise based syntax, like so:
sendMessage("dartToUpperCase", "hello").then(upperCaseResult => console.log("Result is " + upperCaseResult));
and marking the onMessage function as async
but that didn't work either?
@abner Is there anyway for us to return results back to the Javascript VM from the onMessage handler?
I've tried too, but in quickjs_runtime2.dart (where calls to/from QuickJS/Dart are handled) there's an error at line 238:
if (channelFunctions.containsKey(channelName)) { channelFunctions[channelName]!.call(jsonDecode(message)); } else { print('No channel $channelName registered'); }
should be instead
if (channelFunctions.containsKey(channelName)) { return channelFunctions[channelName]!.call(jsonDecode(message)); } else { print('No channel $channelName registered'); }
The missing "return" from call to channelFunctions[channelName]!.call... makes return value always null. If you change that line (i've tried in my project) you got correct return values from Dart.
@abner do you think this patch breaks something (in memory allocation or other critical places) or could be merged upstream ? Thank you for your nice work
I've made a pull-request to my fix to the problem. If anyone other than me could test it, it would be great
I've tested in our project and it doesn't seem to increase memory allocation if called repeatedly or crash the application core. I've found also that the problem is still present in Apple's JavascriptCore.
in QuickJS the Pull Request https://github.com/abner/flutter_js/pull/54 was accepted and already fixed that for QuickJS.
In AndroidCore i need to look into this more carefully. But i do think the way sendMessage was designed allows to manage the integration of Dart -> JS and JS -> Dart more decoupled, so i believe it is better. It is easy to provide multiple implementations using async or sync integrations, like we do to polyfills Xhr, console.log and others examples are already present at flutter_js codebase.
The way the engines (QuickJS and JavascriptCore) allow the Dart integration are different, so decoupled sendMessage was the best effort at the time to allow a symmetric solution.
In my application I do this all the time and the sendMessage
has never been a limitation. Until now =(.
First, let me explain how I achieved this behavior.
In my javascript code I have a utility file for managing cross-platform promises. The function registerPromise
registers a promise with a unique id. From the Dart code, I can call the javascript function resolvePromise
with the promise id and the resulting value to resolve the promise that is being awaited by the javascript code. Check the example below:
Javascript code:
function runDartCodeAndGetResult {
const staticPromise = createStaticPromise()
const promiseId = registerPromise(staticPromise)
sendMessage('channelName', JSON.stringify({ promiseId, other: 'params' }))
return staticPromise.promise
}
createStaticPromise()
is defined here.
Dart code:
javascriptRuntime.onMessage('channelName', (dynamic args) {
final result = doStuffAndGetResult()
final encoded = json.encode(result)
javascriptRuntime.evaluate('resolvePromise(${args['promiseId']}, $encoded)');
});
With this I can always call dart code and get results from Javascript. But there's a catch: the code must asynchronous!.
Until now, it has always been fine to use async code, but now, I have a specific code that should not be async. I can change it to be async, of course, but I really don't wanna do this, it would make the javascript code much more complex than it should be. For this reason I believe it would be very useful to support return values from the onMessage
function (in dart). Besides making it simpler, it would also make synchronous code possible.
I made a PR adding support for JavascriptCore #67. Can you please check it @abner?
Anyone using JavascriptCore can also test it please?
I made a PR adding support for JavascriptCore #67. Can you please check it @abner?
Anyone using JavascriptCore can also test it please?
works on ios jscore but you cannot run async code in onMessage dart callback, for example in that callback i need to fetch something or do something that is async on the dart side, in my opinion
final result = channelFunctions[channelName]!.call(jsonDecode(message));
should be changed to invoke the async callback.