| 
									
										
										
										
											2023-06-01 04:41:12 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | 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> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | </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
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-01 08:25:44 +00:00
										 |  |  | Since fetching assets is asynchronous, it is recommended to create a wrapper | 
					
						
							| 
									
										
										
										
											2023-06-01 04:41:12 +00:00
										 |  |  | `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. |