---
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:

<table><thead><tr>
  <th><a href="#demo">iOS</a></th>
</tr></thead><tbody><tr><td>

![iOS screenshot](pathname:///flutter/ios.png)

</td></tr></tbody></table>

:::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<SheetJSFlutter> {
  // highlight-next-line
  late JavascriptRuntime _engine;

  @override void initState() {
    // highlight-next-line
    _engine = getJavascriptRuntime();
  }
}
```

#### Running SheetJS Scripts

Since fetching assets is asynchronous, it is recommended to create a wrapper
`async` function and sequentially await each script:

```dart
class SheetJSFlutterState extends State<SheetJSFlutter> {
  String _version = '0.0.0';

  @override void initState() {
    _engine = getJavascriptRuntime();
    _initEngine(); // note: this is not `await`-ed
  }

  Future<void> _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<List<dynamic>>` (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<SheetJSFlutter> {
  List<List<dynamic>> _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.

:::

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:

<CodeBlock language="bash">{`\
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 ..`}
</CodeBlock>

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 <https://sheetjs.com/pres.numbers>, parses, converts data to an
array of arrays, and presents the data in a Flutter `Table` widget.