diff --git a/docz/docs/03-demos/05-mobile/06-flutter.md b/docz/docs/03-demos/05-mobile/06-flutter.md
new file mode 100644
index 0000000..7b7adcb
--- /dev/null
+++ b/docz/docs/03-demos/05-mobile/06-flutter.md
@@ -0,0 +1,231 @@
+---
+title: Flutter
+pagination_prev: demos/static/index
+pagination_next: demos/desktop/index
+sidebar_position: 5
+sidebar_custom_props:
+  summary: Dart + JS Interop
+---
+
+import current from '/version.js';
+import CodeBlock from '@theme/CodeBlock';
+
+Dart + Flutter is a cross-platform alternative to [JS + React Native](/docs/demos/mobile/reactnative).
+
+For the iOS and Android targets, the `flutter_js` package wraps JavaScriptCore
+and QuickJS engines respectively.
+
+The [Standalone scripts](/docs/getting-started/installation/standalone) can be
+parsed and evaluated in the wrapped engines.
+
+The "Complete Example" creates an app that looks like the screenshots below:
+
+
+  | iOS+ | 
| +
+
+
+ | 
+
+:::warning Telemetry
+
+Before starting this demo, manually disable telemetry.  On MacOS:
+
+```bash
+dart --disable-telemetry
+dart --disable-analytics
+flutter config --no-analytics
+flutter config --disable-telemetry
+```
+
+:::
+
+## Integration Details
+
+This demo assumes familiarity with Dart and Flutter.
+
+### Loading SheetJS
+
+#### Adding the scripts
+
+The `flutter.assets` property in `pubspec.yaml` specifies assets.  Assuming the
+standalone script and shim are placed in the `scripts` folder, the following
+snippet loads the scripts as assets:
+
+```yaml title="pubspec.yaml"
+flutter:
+  assets:
+    - scripts/xlsx.full.min.js
+    - scripts/shim.min.js
+```
+
+Once loaded, the contents can be loaded with `rootBundle.loadString`:
+
+```dart
+import 'package:flutter/services.dart' show rootBundle;
+
+String shim = await rootBundle.loadString("scripts/shim.min.js");
+String sheetjs = await rootBundle.loadString("scripts/xlsx.full.min.js");
+```
+
+#### Initialization
+
+It is strongly recommended to add the engine to the state of a `StatefulWidget`:
+
+```dart
+import 'package:flutter_js/flutter_js.dart';
+
+class SheetJSFlutterState extends State {
+  // highlight-next-line
+  late JavascriptRuntime _engine;
+
+  @override void initState() {
+    // highlight-next-line
+    _engine = getJavascriptRuntime();
+  }
+}
+```
+
+#### Running SheetJS Scripts
+
+Since fetching assets is asychronous, it is recommended to create a wrapper
+`async` function and sequentially await each script:
+
+```dart
+class SheetJSFlutterState extends State {
+  String _version = '0.0.0';
+
+  @override void initState() {
+    _engine = getJavascriptRuntime();
+    _initEngine(); // note: this is not `await`-ed
+  }
+
+  Future _initEngine() async {
+    /* fetch and evaluate the shim */
+    String shim = await rootBundle.loadString("scripts/shim.min.js");
+    _engine.evaluate(shim);
+    // highlight-start
+    /* fetch and evaluate the main script */
+    String sheetjs = await rootBundle.loadString("scripts/xlsx.full.min.js");
+    _engine.evaluate(sheetjs);
+    // highlight-end
+    /* capture the version string */
+    JsEvalResult vers = _engine.evaluate("XLSX.version");
+    setState(() => _version = vers.stringResult);
+  }
+}
+```
+
+### Reading data
+
+The most common binary data type in Dart is `Uint8List`. It is the data type
+for `http.Response#bodyBytes` and the return type of `File#readAsBytes()`.
+
+The Flutter JS connector offers no simple interop for `Uint8List` data. The data
+should be converted to Base64 before parsing.
+
+The `csv` package provides a special `CsvToListConverter` converter to generate
+`List>` (Dart's spiritual equivalent of the array of arrays):
+
+```dart
+import 'dart:convert';
+import 'package:http/http.dart' as http;
+import 'package:csv/csv.dart';
+
+class SheetJSFlutterState extends State {
+  List> _data = [];
+
+  void _processBytes(Uint8List bytes) {
+    String base64 = base64Encode(bytes);
+    JsEvalResult func = _engine.evaluate("""
+      var wb = XLSX.read('$base64');
+      XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
+    """);
+    String csv = func.stringResult;
+    setState(() { _data = CsvToListConverter(eol: "\n").convert(csv); });
+  }
+```
+
+## Demo
+
+:::note
+
+This demo was tested on an Intel Mac on 2023 May 31 with Flutter 3.10.2,
+Dart 3.0.2, and `flutter_js` 0.7.0
+
+The iOS simulator runs iOS 16.2 on an iPhone 14 Pro Max.
+
+:::
+
+### Base Project
+
+1) Disable telemetry.
+
+```bash
+dart --disable-telemetry
+dart --disable-analytics
+flutter config --no-analytics
+flutter config --disable-telemetry
+```
+
+2) Create a new Flutter project:
+
+```bash
+flutter create sheetjs_flutter
+cd sheetjs_flutter
+```
+
+3) Open the iOS simulator
+
+4) While the iOS simulator is open, start the application:
+
+```bash
+flutter run
+```
+
+Once the app loads in the simulator, stop the terminal process.
+
+5) Install Flutter / Dart dependencies:
+
+```bash
+flutter pub add http csv flutter_js
+```
+
+6) Open `pubspec.yaml` with a text editor. Search for the line that starts with
+`flutter:` (no whitespace) and add the highlighted lines:
+
+```yaml title="pubspec.yaml"
+# The following section is specific to Flutter packages.
+flutter:
+// highlight-start
+  assets:
+    - scripts/xlsx.full.min.js
+    - scripts/shim.min.js
+// highlight-end
+```
+
+7) Download dependencies to the `scripts` folder:
+
+{`\
+mkdir -p scripts
+cd scripts
+curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
+curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
+cd ..`}
+
+
+8) Download [`main.dart`](pathname:///flutter/main.dart) to `lib/main.dart`:
+
+```bash
+curl -L -o lib/main.dart https://docs.sheetjs.com/flutter/main.dart
+```
+
+9) Launch the app:
+
+```bash
+flutter run
+```
+
+The app fetches , parses, converts data to an
+array of arrays, and presents the data in a Flutter `Table` widget.
diff --git a/docz/docusaurus.config.js b/docz/docusaurus.config.js
index b3e9df2..d651c23 100644
--- a/docz/docusaurus.config.js
+++ b/docz/docusaurus.config.js
@@ -145,7 +145,7 @@ const config = {
       prism: {
         theme: lightCodeTheme,
         darkTheme: darkCodeTheme,
-        additionalLanguages: [ "visual-basic", "swift", "java", "csharp", "perl", "ruby", "cpp", "applescript", "liquid", "rust" ],
+        additionalLanguages: [ "visual-basic", "swift", "java", "csharp", "perl", "ruby", "cpp", "applescript", "liquid", "rust", "dart" ],
       },
       liveCodeBlock: {
         playgroundPosition: 'top'
diff --git a/docz/static/flutter/ios.png b/docz/static/flutter/ios.png
new file mode 100644
index 0000000..c5ba8b7
Binary files /dev/null and b/docz/static/flutter/ios.png differ
diff --git a/docz/static/flutter/main.dart b/docz/static/flutter/main.dart
new file mode 100644
index 0000000..5a961bd
--- /dev/null
+++ b/docz/static/flutter/main.dart
@@ -0,0 +1,100 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart' show rootBundle;
+import 'dart:typed_data';
+import 'dart:convert';
+import 'package:http/http.dart' as http;
+import 'package:flutter_js/flutter_js.dart';
+import 'package:csv/csv.dart';
+import 'package:collection/collection.dart';
+
+void main() {
+  runApp(const MyApp());
+}
+
+class MyApp extends StatelessWidget {
+  const MyApp({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      debugShowCheckedModeBanner: false,
+      title: 'SheetJS x Flutter Demo',
+      theme: ThemeData(
+        colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
+        useMaterial3: true,
+      ),
+      home: const SheetJSFlutter(),
+    );
+  }
+}
+
+class SheetJSFlutter extends StatefulWidget {
+  const SheetJSFlutter({super.key});
+
+  @override
+  State createState() => SheetJSFlutterState();
+}
+
+class SheetJSFlutterState extends State {
+  String _version = '0.0.0';
+  List> _data = [];
+  late JavascriptRuntime _engine;
+
+  @override
+  void initState() {
+    super.initState();
+    _async();
+  }
+
+  void _async() async {
+    await _initEngine();
+    await _fetch();
+  }
+
+  Future _initEngine() async {
+    /* load scripts */
+    _engine = getJavascriptRuntime();
+    String shim = await rootBundle.loadString("scripts/shim.min.js");
+    _engine.evaluate(shim);
+    String sheetjs = await rootBundle.loadString("scripts/xlsx.full.min.js");
+    _engine.evaluate(sheetjs);
+    JsEvalResult vers = _engine.evaluate("XLSX.version");
+    setState(() => _version = vers.stringResult);
+  }
+
+  Future _fetch() async {
+    final res = await http.get(Uri.parse("https://sheetjs.com/pres.numbers"));
+    if (res.statusCode == 200)
+      _processBytes(res.bodyBytes);
+    else
+      throw Exception("Failed to fetch file");
+  }
+
+  void _processBytes(Uint8List bytes) {
+    String base64 = base64Encode(bytes);
+    JsEvalResult func = _engine.evaluate("""
+      var wb = XLSX.read('$base64');
+      XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
+    """);
+    setState(() {
+      _data = CsvToListConverter(eol: "\n").convert(func.stringResult);
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
+        title: Text('SheetJS x Flutter $_version'),
+      ),
+      body: Table(
+        children: _data.mapIndexed((R, row) => TableRow(
+          decoration: BoxDecoration(color: R == 0 ? Colors.blue[50] : null),
+          children: row.mapIndexed((C, cell) => Text(cell.toString())).toList()
+        )).toList()
+      ),
+    );
+  }
+}
+