[file_selector_web] migrate to pkg:web (#5413)

This allows this package to be used in a web app compiled to Wasm.

Helps unblock https://github.com/flutter/devtools/issues/6606
This commit is contained in:
Kevin Moore
2023-11-16 16:40:15 -08:00
committed by GitHub
parent 25574f996e
commit 42dbb7573a
8 changed files with 55 additions and 30 deletions

View File

@ -1,3 +1,7 @@
## 0.9.3
* Updates minimum supported SDK version to Dart 3.2.
## 0.9.2+1 ## 0.9.2+1
* Adds pub topics to package metadata. * Adds pub topics to package metadata.

View File

@ -2,23 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:html'; import 'dart:js_interop';
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
import 'package:file_selector_web/src/dom_helper.dart'; import 'package:file_selector_web/src/dom_helper.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import 'package:web/helpers.dart';
void main() { void main() {
group('dom_helper', () { group('dom_helper', () {
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); IntegrationTestWidgetsFlutterBinding.ensureInitialized();
late DomHelper domHelper; late DomHelper domHelper;
late FileUploadInputElement input; late HTMLInputElement input;
FileList? createFileList(List<File> files) { FileList? createFileList(List<File> files) {
final DataTransfer dataTransfer = DataTransfer(); final DataTransfer dataTransfer = DataTransfer();
files.forEach(dataTransfer.items!.add); for (final File e in files) {
return dataTransfer.files as FileList?; // TODO(srujzs): This is necessary in order to support package:web 0.4.0.
// This was not needed with 0.3.0, hence the lint.
// ignore: unnecessary_cast
dataTransfer.items.add(e as JSAny);
}
return dataTransfer.files;
} }
void setFilesAndTriggerEvent(List<File> files, Event event) { void setFilesAndTriggerEvent(List<File> files, Event event) {
@ -36,12 +42,13 @@ void main() {
setUp(() { setUp(() {
domHelper = DomHelper(); domHelper = DomHelper();
input = FileUploadInputElement(); input = (createElementTag('input') as HTMLInputElement)..type = 'file';
}); });
group('getFiles', () { group('getFiles', () {
final File mockFile1 = File(<Object>['123456'], 'file1.txt'); final File mockFile1 =
final File mockFile2 = File(<Object>[], 'file2.txt'); File(<Object>['123456'].jsify as JSArray, 'file1.txt');
final File mockFile2 = File(<Object>[].jsify as JSArray, 'file2.txt');
testWidgets('works', (_) async { testWidgets('works', (_) async {
final Future<List<XFile>> futureFiles = domHelper.getFiles( final Future<List<XFile>> futureFiles = domHelper.getFiles(
@ -114,7 +121,7 @@ void main() {
input: input, input: input,
); );
expect(input.matchesWithAncestors('body'), true); expect(input.matches('body'), true);
expect(input.accept, accept); expect(input.accept, accept);
expect(input.multiple, multiple); expect(input.multiple, multiple);
expect( expect(
@ -128,7 +135,7 @@ void main() {
await futureFile; await futureFile;
// It should be already removed from the DOM after the file is resolved. // It should be already removed from the DOM after the file is resolved.
expect(input.parent, isNull); expect(input.parentElement, isNull);
}); });
}); });
}); });

View File

@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:html';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
@ -10,6 +9,7 @@ import 'package:file_selector_web/file_selector_web.dart';
import 'package:file_selector_web/src/dom_helper.dart'; import 'package:file_selector_web/src/dom_helper.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import 'package:web/helpers.dart';
void main() { void main() {
group('FileSelectorWeb', () { group('FileSelectorWeb', () {
@ -121,7 +121,7 @@ class MockDomHelper implements DomHelper {
Future<List<XFile>> getFiles({ Future<List<XFile>> getFiles({
String accept = '', String accept = '',
bool multiple = false, bool multiple = false,
FileUploadInputElement? input, HTMLInputElement? input,
}) { }) {
expect(accept, _expectedAccept, expect(accept, _expectedAccept,
reason: 'Expected "accept" value does not match.'); reason: 'Expected "accept" value does not match.');

View File

@ -11,6 +11,7 @@ dependencies:
path: ../ path: ../
flutter: flutter:
sdk: flutter sdk: flutter
web: '>=0.3.0 <0.5.0'
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -3,41 +3,46 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:html'; import 'dart:js_interop';
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:web/helpers.dart';
/// Class to manipulate the DOM with the intention of reading files from it. /// Class to manipulate the DOM with the intention of reading files from it.
class DomHelper { class DomHelper {
/// Default constructor, initializes the container DOM element. /// Default constructor, initializes the container DOM element.
DomHelper() { DomHelper() {
final Element body = querySelector('body')!; final Element body = querySelector('body')!;
body.children.add(_container); body.appendChild(_container);
} }
final Element _container = Element.tag('file-selector'); final Element _container = createElementTag('file-selector');
/// Sets the <input /> attributes and waits for a file to be selected. /// Sets the <input /> attributes and waits for a file to be selected.
Future<List<XFile>> getFiles({ Future<List<XFile>> getFiles({
String accept = '', String accept = '',
bool multiple = false, bool multiple = false,
@visibleForTesting FileUploadInputElement? input, @visibleForTesting HTMLInputElement? input,
}) { }) {
final Completer<List<XFile>> completer = Completer<List<XFile>>(); final Completer<List<XFile>> completer = Completer<List<XFile>>();
final FileUploadInputElement inputElement = final HTMLInputElement inputElement =
input ?? FileUploadInputElement(); input ?? (createElementTag('input') as HTMLInputElement)
..type = 'file';
_container.children.add( _container.appendChild(
inputElement inputElement
..accept = accept ..accept = accept
..multiple = multiple, ..multiple = multiple,
); );
inputElement.onChange.first.then((_) { inputElement.onChange.first.then((_) {
final List<XFile> files = final List<XFile> files = Iterable<File>.generate(
inputElement.files!.map(_convertFileToXFile).toList(); inputElement.files!.length,
(int i) => inputElement.files!.item(i)!)
.map(_convertFileToXFile)
.toList();
inputElement.remove(); inputElement.remove();
completer.complete(files); completer.complete(files);
}); });
@ -52,10 +57,13 @@ class DomHelper {
completer.completeError(platformException); completer.completeError(platformException);
}); });
inputElement.addEventListener('cancel', (Event event) { inputElement.addEventListener(
'cancel',
(Event event) {
inputElement.remove(); inputElement.remove();
completer.complete(<XFile>[]); completer.complete(<XFile>[]);
}); }.toJS,
);
// TODO(dit): Reimplement this with the showPicker() API, https://github.com/flutter/flutter/issues/130365 // TODO(dit): Reimplement this with the showPicker() API, https://github.com/flutter/flutter/issues/130365
inputElement.click(); inputElement.click();
@ -64,10 +72,12 @@ class DomHelper {
} }
XFile _convertFileToXFile(File file) => XFile( XFile _convertFileToXFile(File file) => XFile(
Url.createObjectUrl(file), // TODO(srujzs): This is necessary in order to support package:web 0.4.0.
// This was not needed with 0.3.0, hence the lint.
// ignore: unnecessary_cast
URL.createObjectURL(file as JSObject),
name: file.name, name: file.name,
length: file.size, length: file.size,
lastModified: DateTime.fromMillisecondsSinceEpoch( lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified),
file.lastModified ?? DateTime.now().millisecondsSinceEpoch),
); );
} }

View File

@ -2,11 +2,11 @@ name: file_selector_web
description: Web platform implementation of file_selector description: Web platform implementation of file_selector
repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector_web repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector_web
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22
version: 0.9.2+1 version: 0.9.3
environment: environment:
sdk: ">=2.19.0 <4.0.0" sdk: ^3.2.0
flutter: ">=3.7.0" flutter: ">=3.16.0"
flutter: flutter:
plugin: plugin:
@ -22,6 +22,7 @@ dependencies:
sdk: flutter sdk: flutter
flutter_web_plugins: flutter_web_plugins:
sdk: flutter sdk: flutter
web: '>=0.3.0 <0.5.0'
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -51,6 +51,7 @@
- test_api - test_api
- vm_service - vm_service
- wasm - wasm
- web
- yaml - yaml
# Google-owned packages # Google-owned packages
- _discoveryapis_commons - _discoveryapis_commons

View File

@ -68,6 +68,7 @@ final Map<Version, Version> _dartSdkForFlutterSdk = <Version, Version>{
Version(3, 10, 0): Version(3, 0, 0), Version(3, 10, 0): Version(3, 0, 0),
Version(3, 10, 6): Version(3, 0, 6), Version(3, 10, 6): Version(3, 0, 6),
Version(3, 13, 0): Version(3, 1, 0), Version(3, 13, 0): Version(3, 1, 0),
Version(3, 16, 0): Version(3, 2, 0),
}; };
/// Returns the version of the Dart SDK that shipped with the given Flutter /// Returns the version of the Dart SDK that shipped with the given Flutter