Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 34eafd6

Browse files
committed
Add file_selector_web
1 parent 05879a3 commit 34eafd6

File tree

14 files changed

+423
-9
lines changed

14 files changed

+423
-9
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# 0.7.0
2+
3+
- Initial open-source release.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright 2020 The Flutter Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above
9+
copyright notice, this list of conditions and the following
10+
disclaimer in the documentation and/or other materials provided
11+
with the distribution.
12+
* Neither the name of Google Inc. nor the names of its
13+
contributors may be used to endorse or promote products derived
14+
from this software without specific prior written permission.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# file_picker_web
2+
3+
The web implementation of [`file_picker`][1].
4+
5+
**Please set your constraint to `file_picker_web: '>=0.1.y+x <2.0.0'`**
6+
7+
## Backward compatible 1.0.0 version is coming
8+
The plugin has reached a stable API, we guarantee that version `1.0.0` will be backward compatible with `0.1.y+z`.
9+
Please use `file_picker_web: '>=0.1.y+x <2.0.0'` as your dependency constraint to allow a smoother ecosystem migration.
10+
For more details see: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
11+
12+
## Usage
13+
14+
### Import the package
15+
To use this plugin in your Flutter Web app, simply add it as a dependency in
16+
your pubspec alongside the base `file_picker` plugin.
17+
18+
_(This is only temporary: in the future we hope to make this package an
19+
"endorsed" implementation of `file_picker`, so that it is automatically
20+
included in your Flutter Web app when you depend on `package:file_picker`.)_
21+
22+
This is what the above means to your `pubspec.yaml`:
23+
24+
```yaml
25+
...
26+
dependencies:
27+
...
28+
file_picker: ^0.1.0
29+
file_picker_web: ^0.1.0
30+
...
31+
```
32+
33+
### Use the plugin
34+
Once you have the `file_picker_web` dependency in your pubspec, you should
35+
be able to use `package:file_picker` as normal.
36+
37+
[1]: ../file_picker/file_picker
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import 'dart:async';
2+
import 'dart:html';
3+
import 'package:meta/meta.dart';
4+
import 'package:flutter/services.dart';
5+
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
6+
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
7+
8+
/// The web implementation of [FileSelectorPlatform].
9+
///
10+
/// This class implements the `package:file_selector` functionality for the web.
11+
class FileSelectorWeb extends FileSelectorPlatform {
12+
final _container;
13+
14+
/// Registers this class as the default instance of [FileSelectorPlatform].
15+
static void registerWith(Registrar registrar) {
16+
FileSelectorPlatform.instance = FileSelectorWeb();
17+
}
18+
19+
/// Default constructor, initializes _container to a DOM element that we can use
20+
/// to host HTML elements.
21+
/// overrides parameter allows for testing to override functions
22+
FileSelectorWeb({@visibleForTesting Element container})
23+
: _container = container ?? Element.tag('file-selector') {
24+
querySelector('body').children.add(_container);
25+
}
26+
27+
@override
28+
Future<XFile> openFile({
29+
List<XTypeGroup> acceptedTypeGroups,
30+
String initialDirectory,
31+
String confirmButtonText,
32+
}) async {
33+
final files = await _openFiles(acceptedTypeGroups: acceptedTypeGroups);
34+
return files.first;
35+
}
36+
37+
@override
38+
Future<List<XFile>> openFiles({
39+
List<XTypeGroup> acceptedTypeGroups,
40+
String initialDirectory,
41+
String confirmButtonText,
42+
}) async {
43+
return _openFiles(acceptedTypeGroups: acceptedTypeGroups, multiple: true);
44+
}
45+
46+
@override
47+
Future<String> getSavePath({
48+
List<XTypeGroup> acceptedTypeGroups,
49+
String initialDirectory,
50+
String suggestedName,
51+
String confirmButtonText,
52+
}) async =>
53+
null;
54+
55+
@override
56+
Future<String> getDirectoryPath({
57+
String initialDirectory,
58+
String confirmButtonText,
59+
}) async =>
60+
null;
61+
62+
Future<List<XFile>> _openFiles({
63+
List<XTypeGroup> acceptedTypeGroups,
64+
bool multiple = false,
65+
}) async {
66+
final accept = _acceptedTypesToString(acceptedTypeGroups);
67+
final input = _createInputElement()
68+
..accept = accept
69+
..multiple = multiple;
70+
return _getFiles(input);
71+
}
72+
73+
/// Convert list of XTypeGroups to a comma-separated string
74+
static String _acceptedTypesToString(List<XTypeGroup> acceptedTypes) {
75+
if (acceptedTypes == null) return '';
76+
final List<String> allTypes = [];
77+
for (final group in acceptedTypes) {
78+
_assertTypeGroupIsValid(group);
79+
if (group.extensions != null) {
80+
allTypes.addAll(group.extensions.map(_normalizeExtension));
81+
}
82+
if (group.mimeTypes != null) {
83+
allTypes.addAll(group.mimeTypes);
84+
}
85+
if (group.webWildCards != null) {
86+
allTypes.addAll(group.webWildCards);
87+
}
88+
}
89+
return allTypes.join(',');
90+
}
91+
92+
/// Creates a new input element and adds it to the cleared container.
93+
FileUploadInputElement _createInputElement() {
94+
final input = FileUploadInputElement()..id = 'file-selector-input';
95+
_container.children.clear();
96+
_container.children.add(input);
97+
return input;
98+
}
99+
100+
/// For a given input, returns the files selected by an user.
101+
static Future<List<XFile>> _getFiles(FileUploadInputElement input) {
102+
final Completer<List<XFile>> _completer = Completer();
103+
104+
input.onChange.first.then((_) {
105+
final List<XFile> files = input.files.map(_convertFileToXFile).toList();
106+
_completer.complete(files);
107+
});
108+
109+
input.onError.first.then((event) {
110+
final ErrorEvent error = event;
111+
final platformException = PlatformException(
112+
code: error.type,
113+
message: error.message,
114+
);
115+
_completer.completeError(platformException);
116+
});
117+
118+
input.click();
119+
120+
return _completer.future;
121+
}
122+
123+
/// Helper to convert an html.File to an XFile
124+
static XFile _convertFileToXFile(File file) => XFile(
125+
Url.createObjectUrl(file),
126+
name: file.name,
127+
length: file.size,
128+
lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified),
129+
);
130+
131+
/// Make sure that at least one of its fields is populated.
132+
static void _assertTypeGroupIsValid(XTypeGroup group) {
133+
assert(
134+
!((group.extensions == null || group.extensions.isEmpty) &&
135+
(group.mimeTypes == null || group.mimeTypes.isEmpty) &&
136+
(group.webWildCards == null || group.webWildCards.isEmpty)),
137+
'At least one of extensions / mimeTypes / webWildCards is required for web.');
138+
}
139+
140+
/// Append a dot at the beggining if it is not there png -> .png
141+
static String _normalizeExtension(String ext) {
142+
return ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext;
143+
}
144+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: file_selector_web
2+
description: Web platform implementation of file_selector
3+
homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_web
4+
version: 0.7.0
5+
6+
flutter:
7+
plugin:
8+
platforms:
9+
web:
10+
pluginClass: FileSelectorWeb
11+
fileName: file_selector_web.dart
12+
13+
dependencies:
14+
file_selector_platform_interface: ^1.0.0
15+
platform_detect: ^1.4.0
16+
flutter:
17+
sdk: flutter
18+
flutter_web_plugins:
19+
sdk: flutter
20+
meta: ^1.1.7
21+
22+
dev_dependencies:
23+
flutter_test:
24+
sdk: flutter
25+
pedantic: ^1.8.0
26+
integration_test:
27+
path: ../../integration_test
28+
29+
environment:
30+
sdk: ">=2.2.0 <3.0.0"
31+
flutter: ">=1.10.0 <2.0.0"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
7+
void main() {
8+
runApp(MyApp());
9+
}
10+
11+
/// App for testing
12+
class MyApp extends StatefulWidget {
13+
@override
14+
_MyAppState createState() => _MyAppState();
15+
}
16+
17+
class _MyAppState extends State<MyApp> {
18+
@override
19+
Widget build(BuildContext context) {
20+
return Text('Testing... Look at the console output for results!');
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: file_selector_web_tests
2+
publish_to: none
3+
4+
environment:
5+
sdk: ">=2.2.2 <3.0.0"
6+
7+
dependencies:
8+
flutter:
9+
sdk: flutter
10+
flutter_driver:
11+
sdk: flutter
12+
flutter_test:
13+
sdk: flutter
14+
mockito: ^4.1.1
15+
integration_test:
16+
path: ../../../integration_test
17+
file_selector_web:
18+
path: ../
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/bash
2+
if pgrep -lf chromedriver > /dev/null; then
3+
echo "chromedriver is running."
4+
5+
if [ $# -eq 0 ]; then
6+
echo "No target specified, running all tests..."
7+
find test_driver/ -iname *_integration.dart | xargs -n1 -i -t flutter drive -d web-server --web-port=7357 --browser-name=chrome --target='{}'
8+
else
9+
echo "Running test target: $1..."
10+
set -x
11+
flutter drive -d web-server --web-port=7357 --browser-name=chrome --target=$1
12+
fi
13+
14+
else
15+
echo "chromedriver is not running."
16+
fi
17+

0 commit comments

Comments
 (0)