VOOZH about

URL: https://blog.logrocket.com/getting-started-webassembly-flutter-web/

⇱ Getting started with WebAssembly in Flutter Web - LogRocket Blog


2022-09-15
1442
#flutter#webassembly
Ivy Walobwa
131300
👁 Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

Flutter is built with Skia, a 2D graphics library written in C++. Flutter’s Skia engine enables it to create portable and performant applications across various platforms, including the web.

👁 Getting Started With WebAssembly In Flutter Web

Most web browsers understand the JavaScript language; however, the Skia C++ API can run on the web browser through WebAssembly. WebAssembly allows native code to run in the browser by compiling it into a language that the browser understands.

In this tutorial, we’ll introduce WebAssembly and understand its benefits. We’ll also learn how Flutter code interacts with WebAssembly bindings.

🚀 Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

What is WebAssembly?

WebAssembly (Wasm) is a low-level language that runs with near-native performance. It’s a virtual stack machine with a compact binary format that’s meant to be a compile target for high-level languages.

WebAssembly enables engineers to write code in C, C++, Rust, and other high-level languages that run in the browser. Wasm compiles code written in a high-level language to WebAssembly modules. They’re then loaded into a web app and called with JavaScript.

The browser can quickly turn WebAssembly modules into any machine’s instructions.

👁 Webassembly Compiling Chart

Why should we use WebAssembly?

JavaScript has been the primary language that browsers understand. However, when running resource-intensive applications like 3D games, JavaScript tends to be sluggish. For such applications, a near-native experience is required. This is where Wasm comes in.

WebAssembly works alongside JavaScript to provide a near-native speed for our applications. Due to the modules’ small size, Wasm loads and executes faster, enabling highly performant apps on the web.

Wasm enables us to build fast, performant, portable, and memory-safe applications. It’s an open standard designed to run on other platforms, not just the web. Many popular languages have at least some support for WebAssembly.

Dart and WebAssembly interoperability

The Dart web platform enables Dart code to be compiled and run in platforms powered by JavaScript. We can also call existing JavaScript code inside our Dart code, made possible by the JavaScript bindings provided by the js package.

The ability to call JavaScript code from Dart code and Dart code from JavaScript code is termed “Dart-JavaScript interoperability.”


Over 200k developers use LogRocket to create better digital experiences

👁 Image
Learn more →

👁 Dart Js Interoperability

The js package provides annotations and functions that let us specify how our Dart code connects with JavaScript code. The JavaScript API has the WebAssembly object, a namespace for all WebAssembly-related functions, that allows loading WebAssembly modules, creating new memory and table instances, and handling WebAssembly Errors.

WebAssembly has two file formats:

  • .wasm: contains assembly code in binary and is the executable file
  • .wat: contains a human-readable text format of the .wasm file and compiles to .wasm. It is only meant for editing or debugging

Writing WebAssembly code can be painful. Most languages support generating Wasm modules from our source code which we can then load and call using the provided bindings.

We can work with WebAssembly through the JavaScript WebAssembly object in our Dart-web code by using the js bindings.

👁 Dart Web Code Chart

To make use of the js bindings in our Dart code, annotate the method with @JS and add the external keyword to it:

@JS('WebAssembly.instantiate')
external Object instantiate(Object bytesOrBuffer, Object import);

Using WebAssembly in Flutter web apps

We can use various languages to create Wasm modules that we can load into our Flutter apps. In this article, we’ll use AssemblyScript, a TypeScript-like language for WebAssembly, to generate the Wasm modules.

Generating WebAssembly modules using AssemblyScript

To get started, we need to have Node.js installed. You can download Node from Node’s official site.

Next, install npx, an npm package runner, using the command below:

npm i -g npx

Create a new directory and a package.json file. Then install assemblyscript and assemblyscript/loader using the commands below:

mkdir wasm && cd wasm
npm init
npm i --save-dev assemblyscript
npm i --save @assemblyscript/loader

Next, run the command below to scaffold a new project:

npx asinit .

The command will generate assembly and build folders. We’ll write our AssemblyScript modules in the index.ts file and have the generated Wasm code in the build folder.

👁 Folders Screenshot

Next, add the methods below to the index.ts file. The plusOne function adds one to a counter, while the minusOne function subtracts one from the counter.

// The entry file of your WebAssembly module.
export function plusOne(n: i32): i32 {
 return n+1;
}
export function minusOne(n:i32):i32{
 return n - 1;
}

Generate WebAssembly modules by running npm run asbuild in the root directory. This command generates the .wasm and .wat files in the build folder. We’ll make use of the release.wasm file in our Flutter application.

👁 Generated Wasm Files

Using WebAssembly modules in a Flutter app

To use the generated Wasm module, we’ll add the release.wasm file as an asset to our Flutter application. We’ll also use the wasm_interop package, which handles the JavaScript WebAssembly bindings for us and enables us to interact with WebAssembly by calling the exposed methods.

First, create a new Flutter application inside the wasm folder using the flutter create . command. Then, create a new assets/wasm folder and add the generated release.wasm file. Update the pubspec.yaml file to include the assets folder and the wasm_interop package:

dependencies:
 wasm_interop: ^2.0.1
flutter:
 assets:
 - assets/wasm/

Run flutter pub get to add the dependencies.

Update the MyHomePage widget in main.dart file as shown below:

class MyHomePage extends StatefulWidget {
 const MyHomePage({Key? key, required this.title}) : super(key: key);
 final String title;
 @override
 State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
 int _counter = 0;
 void _incrementCounter() {
 }
 void _decrementCounter() {
 }
 @override
 Widget build(BuildContext context) {
 return Scaffold(
 appBar: AppBar(
 title: Text(widget.title),
 ),
 body: Center(
 child: Column(
 mainAxisAlignment: MainAxisAlignment.center,
 children: <Widget>[
 const Text(
 'You current count is:',
 ),
 Text(
 '$_counter',
 style: Theme.of(context).textTheme.headline4,
 ),
 const SizedBox(
 height: 100,
 ),
 Wrap(
 spacing: 100,
 children: [
 ElevatedButton(
 onPressed: _incrementCounter,
 child: const Text('Increment')),
 ElevatedButton(
 onPressed: _decrementCounter,
 child: const Text('Decrement'))
 ],
 )
 ],
 ),
 ),
 );
 }
}

Run flutter run -d chrome in the root directory to serve the app on Chrome. Our app has Increment and Decrement buttons that’ll be hooked to our Wasm functions.

👁 Initial App State Ui

Create a new wasm_loader.dart file and add the WasmLoader class. The WasmLoader class contains our Dart to Wasm interoperability logic.

import 'package:flutter/services.dart' show rootBundle;
import 'package:wasm_interop/wasm_interop.dart';

class WasmLoader {
 WasmLoader({required this.path});
 late Instance? _wasmInstance;
 final String path;
 Future<bool> initialized() async {
 try {
 final bytes = await rootBundle.load(path);
 _wasmInstance = await Instance.fromBufferAsync(bytes.buffer);
 return isLoaded;
 } catch (exc) {
 // ignore: avoid_print
 print('Error on wasm init ${exc.toString()}');
 }
 return false;
 }
 bool get isLoaded => _wasmInstance != null;
 Object callfunction(String name, int input) {
 final func = _wasmInstance?.functions[name];
 return func?.call(input);
 }
}

The code snippet above does the following:

  • Expects a Wasm module path in the class constructor
  • Loads the Wasm module file in the initialized method
  • Compiles and instantiates the Wasm code using the asynchronous Instance.fromBufferAsync method. This method makes use of the WebAssembly.instantiate() JavaScript API
  • Returns the isLoaded state in the initialized method if the Wasm code is successfully initialized
  • Adds a callfunction method that expects a function name and argument, and then makes a call to the function

Finally, update the MyHomePage widget in main.dart file to make use of the WasmLoader:

 late WasmLoader loader;
 int _counter = 0;

 @override
 void initState() {
 super.initState();
 _init();
 }

 Future<void> _init() async {
 loader = WasmLoader(path: 'assets/wasm/release.wasm');
 final isLoaded = await loader.initialized();
 if (isLoaded) {
 setState(() {});
 }
 }

 void _incrementCounter() {
 _counter = loader.callfunction('plusOne', _counter) as int;
 setState(() {});
 }

 void _decrementCounter() {
 _counter = loader.callfunction('minusOne', _counter) as int;
 setState(() {});
 }

The code snippet above does the following:

  • Creates an instance of the WasmLoader with the path of our .wasm file
  • Initializes WasmLoader and updates the state of the applications once initialized
  • Updates the _counter property with the results of calling the plusOne and minusOne functions in our Wasm module

Rerun the application and click on the Increment and Decrement buttons — the counter updates accordingly. You successfully used WebAssembly code in your Flutter app!

Conclusion

In this tutorial, we discussed WebAssembly and looked into some of its benefits in improving your app’s performance. We also looked into how Flutter interacts with JavaScript bindings. Finally, we used AssemblyScript to generate WebAssembly modules that we hooked into our Flutter web application.

With that, you can now use WebAssembly in your Flutter Web applications and enhance their performance. All the code in this article is available on GitHub.

I hope you enjoyed this tutorial!

Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');
     
    // Add to your HTML:
    
    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
     
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
👁 Image
👁 Image
👁 Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

How to build a virtual engineering team with Gemini CLI subagents

Learn how to use Gemini CLI subagents to delegate frontend, backend, testing, and docs tasks to specialized agents with guardrails and clear ownership.

👁 Image
Emmanuel John
Jun 18, 2026 ⋅ 10 min read

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

👁 Image
Emmanuel John
Jun 17, 2026 ⋅ 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

👁 Image
Chizaram Ken
Jun 16, 2026 ⋅ 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension — no new framework required.

👁 Image
Ikeh Akinyemi
Jun 12, 2026 ⋅ 6 min read
View all posts

Hey there, want to help make our blog better?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now