forked from sheetjs/docs.sheetjs.com
232 lines
5.6 KiB
Markdown
232 lines
5.6 KiB
Markdown
|
---
|
||
|
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 asychronous, 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.
|
||
|
|
||
|
:::
|
||
|
|
||
|
### 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:
|
||
|
|
||
|
<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.
|