---
title: Let Data Fly with Flutter
sidebar_label: Dart + Flutter
description: Build data-intensive mobile apps with Dart + Flutter. Seamlessly integrate spreadsheets into your app using SheetJS. Securely process and generate Excel files on the go.
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
sidebar_position: 6
sidebar_custom_props:
  summary: Dart + JS Interop
---
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
Dart[^1] + Flutter[^2] is a popular cross-platform app framework. JavaScript
code can be run through [embedded engines](/docs/demos/engines).
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Dart + Flutter and SheetJS to process spreadsheets. We'll explore
how to use the `flutter_js` package to run JavaScript code and how to pass data
between Dart code and the platform-specific JS engines.
The "Demo" creates an app that looks like the screenshots below:
  | iOS | Android | 
|  |  | 
:::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
:::note pass
This demo assumes familiarity with Dart and Flutter.
:::
For the iOS and Android targets, the `flutter_js` package[^3] wraps JavaScriptCore[^4]
and QuickJS[^5] engines respectively.
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
can be parsed and evaluated in the wrapped engines.
### 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 asynchronous, it is recommended to create a wrapper
`async` function and sequentially await each script:
```dart
class SheetJSFlutterState extends State {
  String _version = '0.0.0';
  late JavascriptRuntime _engine;
  @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 following diagram depicts the workbook waltz:
```mermaid
flowchart LR
  file[(file data\nUint8List)]
  subgraph SheetJS operations
    base64(Base64\nstring)
    wb((SheetJS\nWorkbook))
    csv(CSV\nstring)
  end
  lld(List of\nLists)
  tbl{{Data\nTable}}
  file --> |`base64Encode`\nDart| base64
  base64 --> |`XLSX.read`\nParse Bytes| wb
  wb --> |`sheet_to_csv`\nExtract Data| csv
  csv --> |`CsvToListConverter`\nDart| lld
  lld --> |`Table`\nFlutter| tbl
```
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 using `base64Encode` before parsing.
Once passed into the JS engine, the SheetJS `read` function[^6] can read the
Base64-encoded string and the `sheet_to_csv` utility function[^7] can generate
a CSV string from a worksheet. This string can be pulled back into Dart code.
The `csv` package provides a special `CsvToListConverter` converter to generate
`List>` (Dart's spiritual equivalent of the array of arrays).
The following snippet generates `List>` from a Dart `Uint8List`:
```dart
import 'dart:convert';
import 'package:csv/csv.dart';
class SheetJSFlutterState extends State {
  List> _data = [];
  late JavascriptRuntime _engine;
  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-08-18 with Flutter 3.13.0, Dart
3.1.0, and `flutter_js` 0.8.0.
The iOS simulator runs iOS 16.2 on an iPhone 14 Pro Max.
The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.
:::
0) Follow the official "Install" instructions for Flutter[^8].
Run `flutter doctor` and confirm the following items are checked:
```
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Android Studio (version 2022.1)
```
(the actual version numbers may differ)
Installation Notes (click to hide)
:::caution pass
In local testing, there were issues with the Android toolchain:
```
error: Android sdkmanager not found. Update to the latest Android SDK and ensure that the cmdline-tools are installed to resolve this.
```
This was fixed by switching to Java 20, installing `Android SDK 33`, and rolling
back to `Android SDK Command-Line Tools (revision: 10.0)`
:::
:::caution pass
If Google Chrome is not installed, `flutter doctor` will show an issue:
```
[✗] Chrome - develop for the web (Cannot find Chrome executable at
    /Applications/Google Chrome.app/Contents/MacOS/Google Chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
```
If Chromium is installed, the environment variable should be manually assigned:
```bash
export CHROME_EXECUTABLE=/Applications/Chromium.app/Contents/MacOS/Chromium
```
:::