@@ -18,6 +18,7 @@ final String _kAcceptVideoMimeType = 'video/3gpp,video/x-m4v,video/mp4,video/*';
1818/// This class implements the `package:image_picker` functionality for the web.
1919class ImagePickerPlugin extends ImagePickerPlatform {
2020 final ImagePickerPluginTestOverrides ? _overrides;
21+
2122 bool get _hasOverrides => _overrides != null ;
2223
2324 late html.Element _target;
@@ -115,9 +116,13 @@ class ImagePickerPlugin extends ImagePickerPlatform {
115116 double ? maxHeight,
116117 int ? imageQuality,
117118 CameraDevice preferredCameraDevice = CameraDevice .rear,
118- }) {
119+ }) async {
119120 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;
121126 }
122127
123128 /// Returns an [XFile] containing the video that was picked.
@@ -137,25 +142,48 @@ class ImagePickerPlugin extends ImagePickerPlatform {
137142 required ImageSource source,
138143 CameraDevice preferredCameraDevice = CameraDevice .rear,
139144 Duration ? maxDuration,
140- }) {
145+ }) async {
141146 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 );
143162 }
144163
145164 /// 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.
147166 ///
148167 /// `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+ ///
149172 /// See https://caniuse.com/#feat=html-media-capture
150173 @visibleForTesting
151- Future <XFile > getFile ({
174+ Future <List < XFile >> getFiles ({
152175 String ? accept,
153176 String ? capture,
177+ bool multiple = false ,
154178 }) {
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 ;
157184 _injectAndActivate (input);
158- return _getSelectedXFile (input);
185+
186+ return _getSelectedXFiles (input);
159187 }
160188
161189 // DOM methods
@@ -171,34 +199,31 @@ class ImagePickerPlugin extends ImagePickerPlatform {
171199 return null ;
172200 }
173201
174- html.File ? _getFileFromInput (html.FileUploadInputElement input) {
202+ List < html.File > ? _getFilesFromInput (html.FileUploadInputElement input) {
175203 if (_hasOverrides) {
176- return _overrides! .getFileFromInput (input);
204+ return _overrides! .getMultipleFilesFromInput (input);
177205 }
178- return input.files? .first ;
206+ return input.files;
179207 }
180208
181209 /// 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) {
184212 final html.FileUploadInputElement input =
185213 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);
192215 }
193216
194217 /// Monitors an <input type="file"> and returns the selected file.
195218 Future <PickedFile > _getSelectedFile (html.FileUploadInputElement input) {
196219 final Completer <PickedFile > _completer = Completer <PickedFile >();
197220 // Observe the input until we can return something
198221 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+ ));
202227 }
203228 });
204229 input.onError.first.then ((event) {
@@ -212,13 +237,24 @@ class ImagePickerPlugin extends ImagePickerPlatform {
212237 return _completer.future;
213238 }
214239
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 >>();
217243 // Observe the input until we can return something
218244 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 ());
222258 }
223259 });
224260 input.onError.first.then ((event) {
@@ -248,12 +284,18 @@ class ImagePickerPlugin extends ImagePickerPlatform {
248284 /// Creates an input element that accepts certain file types, and
249285 /// allows to `capture` from the device's cameras (where supported)
250286 @visibleForTesting
251- html.Element createInputElement (String ? accept, String ? capture) {
287+ html.Element createInputElement (
288+ String ? accept,
289+ String ? capture, {
290+ bool multiple = false ,
291+ }) {
252292 if (_hasOverrides) {
253293 return _overrides! .createInputElement (accept, capture);
254294 }
255295
256- html.Element element = html.FileUploadInputElement ()..accept = accept;
296+ html.Element element = html.FileUploadInputElement ()
297+ ..accept = accept
298+ ..multiple = multiple;
257299
258300 if (capture != null ) {
259301 element.setAttribute ('capture' , capture);
@@ -278,18 +320,17 @@ typedef OverrideCreateInputFunction = html.Element Function(
278320 String ? capture,
279321);
280322
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.
282324@visibleForTesting
283- typedef OverrideExtractFilesFromInputFunction = html.File Function (
284- html.Element ? input,
285- );
325+ typedef OverrideExtractMultipleFilesFromInputFunction = List <html.File >
326+ Function (html.Element ? input);
286327
287328/// Overrides for some of the functionality above.
288329@visibleForTesting
289330class ImagePickerPluginTestOverrides {
290331 /// Override the creation of the input element.
291332 late OverrideCreateInputFunction createInputElement;
292333
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 ;
295336}
0 commit comments