[0:00]Hello, my name is Joe. I am a Google developer expert for Flutter and Dart and I work as a senior software engineer at the Wing. I'm also the co-organizer of the Flutter Community meetup here in London. And today I want to share with you my journey to building something like this where I can run some JavaScript, get some results from it, and render some Flutter UI from there. And I'm not using a web view for that. Before we dive into the details, I want to talk a little bit about what is JavaScript and what is Dart. Both of these languages are very similar in terms of features, in terms of syntax, even. They both support object-oriented programming, classes, mix-ins, extension methods. And they're both very popular. I think JavaScript has more than 17 million developers worldwide and Dart is a little bit less, I think it's more than 1 million developers. So very popular languages that have been around for a while. However, when it comes to the runtime, they're very different. JavaScript usually runs on Node.js or in the browser, whereas Dart runs on its own virtual machine. And that's why, by default, it's not possible to run JavaScript code in a Dart application. So how can we overcome this problem? There are different solutions, of course. For example, if you want to reuse some code that you have written in JavaScript, you can always rewrite it in Dart. That's one option. Or you can use a web view. That's also another option, probably the easiest to implement. However, in my use case, I didn't want to use a web view. So I went on a quest and I found a couple of packages on pub.dev. The first one is called Dart JS, which provides a low-level interoperability with JavaScript. So you can basically call JavaScript methods from Dart or listen to events from JavaScript, but it requires that you are compiling your Dart application to JavaScript. And in my use case, I was building a mobile application. So I couldn't use Dart JS. Then I found another package that is called Flutter JS. And this one is very interesting. It supports both iOS and Android, and it uses JavaScript core on iOS and on Android, it uses QuickJS. So it basically embeds a JavaScript engine in your Flutter application. And this is probably the most used package that you will find. It also has a lot of open issues and it's not well maintained. So I decided not to use it. So I went on a quest again and I started searching on Google and found this very interesting project that is called QuickJS. And it's a small and embeddable JavaScript engine. So it has a very small memory footprint, which makes it suitable for embedded devices and for mobile applications. So I thought, okay, this is a very interesting project. Let me try to see if I can integrate it with Flutter. So I found a couple of articles and I started digging deeper. And that's how I found FFI. FFI stands for Foreign Function Interface. And it's a mechanism that allows you to call functions written in other languages from Dart. And it's basically a bridge between Dart and the C API. So with FFI, I can basically call C functions and interact with C code from Dart. And because QuickJS is written in C, I thought, okay, this is a very interesting approach. Let me try to combine FFI with QuickJS. And that's exactly what I did. I created a plugin which is called Flutter QuickJS, which allows you to run JavaScript code from Flutter without using a web view. So let me show you how it works with a small demo. The first thing that you need to do is to create an instance of the JavaScript engine. So you can basically call QuickJS, create and this will return a JavaScript engine instance. And this is basically a pointer to the JavaScript engine that you have created. Then you can use this engine to evaluate some JavaScript code. So in this case, I'm calling the QuickJS evaluate method and I'm passing a string, which is basically an empty string because I'm just creating a runtime. And I'm not evaluating anything specific here. And it will return a JavaScript value. And the JavaScript value is also a pointer to the value that has been returned by the JavaScript engine. Once you're done with your JavaScript engine, you can dispose it. So you can basically call QuickJS dispose and this will clean up the memory that has been allocated. So this is very important. So this is the basic usage of the plugin. Let's see how we can use it to build something like this. So here I'm using an example to run some JavaScript code. First, I'm defining a constant, which is called JavaScript code. And this is basically a string that contains a JavaScript function. And this function is called get message and it returns a JSON object. The JSON object has two properties, a message and a timestamp. Then I'm creating an instance of the QuickJS engine, as I showed you previously. And then I'm evaluating the JavaScript code that I have defined here. And this will return a JavaScript value. Then I'm taking this JavaScript value and I'm calling a method on it. So I'm basically calling the method get message. And this will return another JavaScript value, which is basically the result of the function. And I'm converting this JavaScript value to a JSON string. So I can basically decode it in Dart and get the values from it. And then I'm creating a message object from the JSON string. And here is where the UI comes into play. So I'm basically taking the message and the timestamp from the message object and I'm rendering them in a Flutter UI. So this is how it works. And as I mentioned, the plugin uses FFI. So you can basically find some native code here and this is basically a simple wrapper around the QuickJS C API. And I'm basically exposing a couple of methods that I can call from Dart. So let's take a look at the QuickJS create function. As you can see, I'm basically initializing the QuickJS runtime and context. And then I'm returning a pointer to the context. And then when I want to dispose the QuickJS engine, I'm basically freeing the context and the runtime that I have created. And then the evaluate method basically takes the context and the JavaScript code that I want to evaluate. And then it will return a value from QuickJS. This value is then converted to a JavaScript value in Dart. And this is basically how it works. So as you can see, with FFI, it's very easy to integrate C code with Dart. However, there are some limitations that you need to be aware of. First, the plugin only supports iOS and Android. And this is because it relies on the C libraries that are provided by QuickJS. So if you want to support other platforms, you need to compile QuickJS for those platforms. And it's not always easy. For example, for web, it's not possible to use FFI. And also, there are no built-in modules for QuickJS. So if you want to use something like HTTP or file system, you need to implement it yourself. And also, there is no garbage collection. So you need to manually manage the memory that you allocate. And as you can see, when I was creating the QuickJS engine, I was always disposing it after I was done with it. And this is very important to avoid memory leaks. There are some alternatives to QuickJS, for example, JavaScript core. That's also a very good option, but it's only available on iOS and macOS. So if you want to support Android, you need to use something else. Or you can use a web view. That's also another option, probably the easiest to implement. And it's cross-platform. However, as I mentioned, in my use case, I didn't want to use a web view. So I decided to go with QuickJS. And that's it for my talk. If you have any questions, feel free to reach out to me on Twitter or on GitHub. And thank you for your attention.
Watch on YouTube
Share
MORE TRANSCRIPTS



