forked from sheetjs/docs.sheetjs.com
		
	flutter
This commit is contained in:
		
							parent
							
								
									686b5c55af
								
							
						
					
					
						commit
						aa59cc10ad
					
				
							
								
								
									
										231
									
								
								docz/docs/03-demos/05-mobile/06-flutter.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										231
									
								
								docz/docs/03-demos/05-mobile/06-flutter.md
									
									
									
									
									
										Normal file
									
								
							| @ -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: | ||||
| 
 | ||||
| <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 | ||||
| 
 | ||||
| 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. | ||||
| @ -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' | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								docz/static/flutter/ios.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								docz/static/flutter/ios.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 82 KiB | 
							
								
								
									
										100
									
								
								docz/static/flutter/main.dart
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										100
									
								
								docz/static/flutter/main.dart
									
									
									
									
									
										Normal file
									
								
							| @ -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<SheetJSFlutter> createState() => SheetJSFlutterState(); | ||||
| } | ||||
| 
 | ||||
| class SheetJSFlutterState extends State<SheetJSFlutter> { | ||||
|   String _version = '0.0.0'; | ||||
|   List<List<dynamic>> _data = []; | ||||
|   late JavascriptRuntime _engine; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     _async(); | ||||
|   } | ||||
| 
 | ||||
|   void _async() async { | ||||
|     await _initEngine(); | ||||
|     await _fetch(); | ||||
|   } | ||||
| 
 | ||||
|   Future<void> _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<void> _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() | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user