@@ -18,6 +18,7 @@ final String _kAcceptVideoMimeType = 'video/3gpp,video/x-m4v,video/mp4,video/*';
18
18
/// This class implements the `package:image_picker` functionality for the web.
19
19
class ImagePickerPlugin extends ImagePickerPlatform {
20
20
final ImagePickerPluginTestOverrides ? _overrides;
21
+
21
22
bool get _hasOverrides => _overrides != null ;
22
23
23
24
late html.Element _target;
@@ -115,9 +116,13 @@ class ImagePickerPlugin extends ImagePickerPlatform {
115
116
double ? maxHeight,
116
117
int ? imageQuality,
117
118
CameraDevice preferredCameraDevice = CameraDevice .rear,
118
- }) {
119
+ }) async {
119
120
String ? capture = computeCaptureAttribute (source, preferredCameraDevice);
120
- return getFile (accept: _kAcceptImageMimeType, capture: capture);
121
+ List <XFile > files = await getFiles (
122
+ accept: _kAcceptImageMimeType,
123
+ capture: capture,
124
+ );
125
+ return files.first;
121
126
}
122
127
123
128
/// Returns an [XFile] containing the video that was picked.
@@ -137,25 +142,48 @@ class ImagePickerPlugin extends ImagePickerPlatform {
137
142
required ImageSource source,
138
143
CameraDevice preferredCameraDevice = CameraDevice .rear,
139
144
Duration ? maxDuration,
140
- }) {
145
+ }) async {
141
146
String ? capture = computeCaptureAttribute (source, preferredCameraDevice);
142
- return getFile (accept: _kAcceptVideoMimeType, capture: capture);
147
+ List <XFile > files = await getFiles (
148
+ accept: _kAcceptVideoMimeType,
149
+ capture: capture,
150
+ );
151
+ return files.first;
152
+ }
153
+
154
+ /// Injects a file input, and returns a list of XFile that the user selected locally.
155
+ @override
156
+ Future <List <XFile >> getMultiImage ({
157
+ double ? maxWidth,
158
+ double ? maxHeight,
159
+ int ? imageQuality,
160
+ }) {
161
+ return getFiles (accept: _kAcceptImageMimeType, multiple: true );
143
162
}
144
163
145
164
/// Injects a file input with the specified accept+capture attributes, and
146
- /// returns the PickedFile that the user selected locally.
165
+ /// returns a list of XFile that the user selected locally.
147
166
///
148
167
/// `capture` is only supported in mobile browsers.
168
+ ///
169
+ /// `multiple` can be passed to allow for multiple selection of files. Defaults
170
+ /// to false.
171
+ ///
149
172
/// See https://caniuse.com/#feat=html-media-capture
150
173
@visibleForTesting
151
- Future <XFile > getFile ({
174
+ Future <List < XFile >> getFiles ({
152
175
String ? accept,
153
176
String ? capture,
177
+ bool multiple = false ,
154
178
}) {
155
- html.FileUploadInputElement input =
156
- createInputElement (accept, capture) as html.FileUploadInputElement ;
179
+ html.FileUploadInputElement input = createInputElement (
180
+ accept,
181
+ capture,
182
+ multiple: multiple,
183
+ ) as html.FileUploadInputElement ;
157
184
_injectAndActivate (input);
158
- return _getSelectedXFile (input);
185
+
186
+ return _getSelectedXFiles (input);
159
187
}
160
188
161
189
// DOM methods
@@ -171,34 +199,31 @@ class ImagePickerPlugin extends ImagePickerPlatform {
171
199
return null ;
172
200
}
173
201
174
- html.File ? _getFileFromInput (html.FileUploadInputElement input) {
202
+ List < html.File > ? _getFilesFromInput (html.FileUploadInputElement input) {
175
203
if (_hasOverrides) {
176
- return _overrides! .getFileFromInput (input);
204
+ return _overrides! .getMultipleFilesFromInput (input);
177
205
}
178
- return input.files? .first ;
206
+ return input.files;
179
207
}
180
208
181
209
/// Handles the OnChange event from a FileUploadInputElement object
182
- /// Returns the objectURL of the selected file .
183
- String ? _handleOnChangeEvent (html.Event event) {
210
+ /// Returns a list of selected files .
211
+ List <html. File > ? _handleOnChangeEvent (html.Event event) {
184
212
final html.FileUploadInputElement input =
185
213
event.target as html.FileUploadInputElement ;
186
- final html.File ? file = _getFileFromInput (input);
187
-
188
- if (file != null ) {
189
- return html.Url .createObjectUrl (file);
190
- }
191
- return null ;
214
+ return _getFilesFromInput (input);
192
215
}
193
216
194
217
/// Monitors an <input type="file"> and returns the selected file.
195
218
Future <PickedFile > _getSelectedFile (html.FileUploadInputElement input) {
196
219
final Completer <PickedFile > _completer = Completer <PickedFile >();
197
220
// Observe the input until we can return something
198
221
input.onChange.first.then ((event) {
199
- final objectUrl = _handleOnChangeEvent (event);
200
- if (! _completer.isCompleted && objectUrl != null ) {
201
- _completer.complete (PickedFile (objectUrl));
222
+ final files = _handleOnChangeEvent (event);
223
+ if (! _completer.isCompleted && files != null ) {
224
+ _completer.complete (PickedFile (
225
+ html.Url .createObjectUrl (files.first),
226
+ ));
202
227
}
203
228
});
204
229
input.onError.first.then ((event) {
@@ -212,13 +237,24 @@ class ImagePickerPlugin extends ImagePickerPlatform {
212
237
return _completer.future;
213
238
}
214
239
215
- Future <XFile > _getSelectedXFile (html.FileUploadInputElement input) {
216
- final Completer <XFile > _completer = Completer <XFile >();
240
+ /// Monitors an <input type="file"> and returns the selected file(s).
241
+ Future <List <XFile >> _getSelectedXFiles (html.FileUploadInputElement input) {
242
+ final Completer <List <XFile >> _completer = Completer <List <XFile >>();
217
243
// Observe the input until we can return something
218
244
input.onChange.first.then ((event) {
219
- final objectUrl = _handleOnChangeEvent (event);
220
- if (! _completer.isCompleted && objectUrl != null ) {
221
- _completer.complete (XFile (objectUrl));
245
+ final files = _handleOnChangeEvent (event);
246
+ if (! _completer.isCompleted && files != null ) {
247
+ _completer.complete (files
248
+ .map ((file) => XFile (
249
+ html.Url .createObjectUrl (file),
250
+ name: file.name,
251
+ length: file.size,
252
+ lastModified: DateTime .fromMillisecondsSinceEpoch (
253
+ file.lastModified ?? DateTime .now ().millisecondsSinceEpoch,
254
+ ),
255
+ mimeType: file.type,
256
+ ))
257
+ .toList ());
222
258
}
223
259
});
224
260
input.onError.first.then ((event) {
@@ -248,12 +284,18 @@ class ImagePickerPlugin extends ImagePickerPlatform {
248
284
/// Creates an input element that accepts certain file types, and
249
285
/// allows to `capture` from the device's cameras (where supported)
250
286
@visibleForTesting
251
- html.Element createInputElement (String ? accept, String ? capture) {
287
+ html.Element createInputElement (
288
+ String ? accept,
289
+ String ? capture, {
290
+ bool multiple = false ,
291
+ }) {
252
292
if (_hasOverrides) {
253
293
return _overrides! .createInputElement (accept, capture);
254
294
}
255
295
256
- html.Element element = html.FileUploadInputElement ()..accept = accept;
296
+ html.Element element = html.FileUploadInputElement ()
297
+ ..accept = accept
298
+ ..multiple = multiple;
257
299
258
300
if (capture != null ) {
259
301
element.setAttribute ('capture' , capture);
@@ -278,18 +320,17 @@ typedef OverrideCreateInputFunction = html.Element Function(
278
320
String ? capture,
279
321
);
280
322
281
- /// A function that extracts a [html.File] from the file `input` passed in.
323
+ /// A function that extracts list of files from the file `input` passed in.
282
324
@visibleForTesting
283
- typedef OverrideExtractFilesFromInputFunction = html.File Function (
284
- html.Element ? input,
285
- );
325
+ typedef OverrideExtractMultipleFilesFromInputFunction = List <html.File >
326
+ Function (html.Element ? input);
286
327
287
328
/// Overrides for some of the functionality above.
288
329
@visibleForTesting
289
330
class ImagePickerPluginTestOverrides {
290
331
/// Override the creation of the input element.
291
332
late OverrideCreateInputFunction createInputElement;
292
333
293
- /// Override the extraction of the selected file from an input element.
294
- late OverrideExtractFilesFromInputFunction getFileFromInput ;
334
+ /// Override the extraction of the selected files from an input element.
335
+ late OverrideExtractMultipleFilesFromInputFunction getMultipleFilesFromInput ;
295
336
}
0 commit comments