Skip to content

Commit b7317cb

Browse files
balvinderzLouiseHsu
authored andcommitted
[image_picker_for_web] migrates to package:web (flutter#5799)
Updates the web implementation of `image_picker_for_web` to `package:web`. ### Issues * Fixes flutter/flutter#139751
1 parent 83bcfbc commit b7317cb

File tree

8 files changed

+211
-155
lines changed

8 files changed

+211
-155
lines changed

packages/image_picker/image_picker_for_web/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
## NEXT
1+
## 3.0.3
22

3-
* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1.
3+
* Migrates package and tests to `package:web`.
4+
* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3.
45

56
## 3.0.2
67

packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,29 @@
33
// found in the LICENSE file.
44

55
import 'dart:convert';
6-
import 'dart:html' as html;
6+
import 'dart:js_interop';
77
import 'dart:typed_data';
88

99
import 'package:flutter_test/flutter_test.dart';
1010
import 'package:image_picker_for_web/image_picker_for_web.dart';
1111
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
1212
import 'package:integration_test/integration_test.dart';
13+
import 'package:web/web.dart' as web;
1314

1415
const String expectedStringContents = 'Hello, world!';
1516
const String otherStringContents = 'Hello again, world!';
1617
final Uint8List bytes = const Utf8Encoder().convert(expectedStringContents);
1718
final Uint8List otherBytes = const Utf8Encoder().convert(otherStringContents);
18-
final Map<String, dynamic> options = <String, dynamic>{
19-
'type': 'text/plain',
20-
'lastModified': DateTime.utc(2017, 12, 13).millisecondsSinceEpoch,
21-
};
22-
final html.File textFile = html.File(<Uint8List>[bytes], 'hello.txt', options);
23-
final html.File secondTextFile =
24-
html.File(<Uint8List>[otherBytes], 'secondFile.txt');
19+
// TODO(dit): When web:0.6.0 lands, move `type` to the [web.FilePropertyBag] constructor.
20+
// See: https://github.com/dart-lang/web/pull/197
21+
final web.FilePropertyBag options = web.FilePropertyBag(
22+
lastModified: DateTime.utc(2017, 12, 13).millisecondsSinceEpoch,
23+
)..type = 'text/plain';
24+
25+
final web.File textFile =
26+
web.File(<JSUint8Array>[bytes.toJS].toJS, 'hello.txt', options);
27+
final web.File secondTextFile =
28+
web.File(<JSUint8Array>[otherBytes.toJS].toJS, 'secondFile.txt');
2529

2630
void main() {
2731
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@@ -36,12 +40,12 @@ void main() {
3640
testWidgets('getImageFromSource can select a file', (
3741
WidgetTester _,
3842
) async {
39-
final html.FileUploadInputElement mockInput = html.FileUploadInputElement();
40-
43+
final web.HTMLInputElement mockInput = web.HTMLInputElement()
44+
..type = 'file';
4145
final ImagePickerPluginTestOverrides overrides =
4246
ImagePickerPluginTestOverrides()
4347
..createInputElement = ((_, __) => mockInput)
44-
..getMultipleFilesFromInput = ((_) => <html.File>[textFile]);
48+
..getMultipleFilesFromInput = ((_) => <web.File>[textFile]);
4549

4650
final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides);
4751

@@ -50,11 +54,12 @@ void main() {
5054
source: ImageSource.camera,
5155
);
5256

53-
expect(html.querySelector('flt-image-picker-inputs')?.children.isEmpty,
54-
isFalse);
57+
expect(
58+
web.document.querySelector('flt-image-picker-inputs')?.children.length,
59+
isNonZero);
5560

5661
// Mock the browser behavior of selecting a file...
57-
mockInput.dispatchEvent(html.Event('change'));
62+
mockInput.dispatchEvent(web.Event('change'));
5863

5964
// Now the file should be available
6065
expect(image, completes);
@@ -69,30 +74,32 @@ void main() {
6974
expect(
7075
file.lastModified(),
7176
completion(
72-
DateTime.fromMillisecondsSinceEpoch(textFile.lastModified!),
77+
DateTime.fromMillisecondsSinceEpoch(textFile.lastModified),
7378
));
74-
expect(html.querySelector('flt-image-picker-inputs')?.children.isEmpty,
75-
isTrue);
79+
expect(
80+
web.document.querySelector('flt-image-picker-inputs')?.children.length,
81+
isZero);
7682
});
7783

7884
testWidgets('getMultiImageWithOptions can select multiple files', (
7985
WidgetTester _,
8086
) async {
81-
final html.FileUploadInputElement mockInput = html.FileUploadInputElement();
87+
final web.HTMLInputElement mockInput = web.HTMLInputElement()
88+
..type = 'file';
8289

8390
final ImagePickerPluginTestOverrides overrides =
8491
ImagePickerPluginTestOverrides()
8592
..createInputElement = ((_, __) => mockInput)
8693
..getMultipleFilesFromInput =
87-
((_) => <html.File>[textFile, secondTextFile]);
94+
((_) => <web.File>[textFile, secondTextFile]);
8895

8996
final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides);
9097

9198
// Init the pick file dialog...
9299
final Future<List<XFile>> files = plugin.getMultiImageWithOptions();
93100

94101
// Mock the browser behavior of selecting a file...
95-
mockInput.dispatchEvent(html.Event('change'));
102+
mockInput.dispatchEvent(web.Event('change'));
96103

97104
// Now the file should be available
98105
expect(files, completes);
@@ -108,13 +115,14 @@ void main() {
108115
});
109116

110117
testWidgets('getMedia can select multiple files', (WidgetTester _) async {
111-
final html.FileUploadInputElement mockInput = html.FileUploadInputElement();
118+
final web.HTMLInputElement mockInput = web.HTMLInputElement()
119+
..type = 'file';
112120

113121
final ImagePickerPluginTestOverrides overrides =
114122
ImagePickerPluginTestOverrides()
115123
..createInputElement = ((_, __) => mockInput)
116124
..getMultipleFilesFromInput =
117-
((_) => <html.File>[textFile, secondTextFile]);
125+
((_) => <web.File>[textFile, secondTextFile]);
118126

119127
final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides);
120128

@@ -123,7 +131,7 @@ void main() {
123131
plugin.getMedia(options: const MediaOptions(allowMultiple: true));
124132

125133
// Mock the browser behavior of selecting a file...
126-
mockInput.dispatchEvent(html.Event('change'));
134+
mockInput.dispatchEvent(web.Event('change'));
127135

128136
// Now the file should be available
129137
expect(files, completes);
@@ -139,20 +147,20 @@ void main() {
139147
});
140148

141149
group('cancel event', () {
142-
late html.FileUploadInputElement mockInput;
150+
late web.HTMLInputElement mockInput;
143151
late ImagePickerPluginTestOverrides overrides;
144152
late ImagePickerPlugin plugin;
145153

146154
setUp(() {
147-
mockInput = html.FileUploadInputElement();
155+
mockInput = web.HTMLInputElement()..type = 'file';
148156
overrides = ImagePickerPluginTestOverrides()
149157
..createInputElement = ((_, __) => mockInput)
150-
..getMultipleFilesFromInput = ((_) => <html.File>[textFile]);
158+
..getMultipleFilesFromInput = ((_) => <web.File>[textFile]);
151159
plugin = ImagePickerPlugin(overrides: overrides);
152160
});
153161

154162
void mockCancel() {
155-
mockInput.dispatchEvent(html.Event('cancel'));
163+
mockInput.dispatchEvent(web.Event('cancel'));
156164
}
157165

158166
testWidgets('getFiles - returns empty list', (WidgetTester _) async {
@@ -226,61 +234,61 @@ void main() {
226234

227235
group('createInputElement', () {
228236
testWidgets('accept: any, capture: null', (WidgetTester tester) async {
229-
final html.Element input = plugin.createInputElement('any', null);
237+
final web.Element input = plugin.createInputElement('any', null);
230238

231-
expect(input.attributes, containsPair('accept', 'any'));
232-
expect(input.attributes, isNot(contains('capture')));
233-
expect(input.attributes, isNot(contains('multiple')));
239+
expect(input.getAttribute('accept'), 'any');
240+
expect(input.hasAttribute('capture'), false);
241+
expect(input.hasAttribute('multiple'), false);
234242
});
235243

236244
testWidgets('accept: any, capture: something', (WidgetTester tester) async {
237-
final html.Element input = plugin.createInputElement('any', 'something');
245+
final web.Element input = plugin.createInputElement('any', 'something');
238246

239-
expect(input.attributes, containsPair('accept', 'any'));
240-
expect(input.attributes, containsPair('capture', 'something'));
241-
expect(input.attributes, isNot(contains('multiple')));
247+
expect(input.getAttribute('accept'), 'any');
248+
expect(input.getAttribute('capture'), 'something');
249+
expect(input.hasAttribute('multiple'), false);
242250
});
243251

244252
testWidgets('accept: any, capture: null, multi: true',
245253
(WidgetTester tester) async {
246-
final html.Element input =
254+
final web.Element input =
247255
plugin.createInputElement('any', null, multiple: true);
248256

249-
expect(input.attributes, containsPair('accept', 'any'));
250-
expect(input.attributes, isNot(contains('capture')));
251-
expect(input.attributes, contains('multiple'));
257+
expect(input.getAttribute('accept'), 'any');
258+
expect(input.hasAttribute('capture'), false);
259+
expect(input.hasAttribute('multiple'), true);
252260
});
253261

254262
testWidgets('accept: any, capture: something, multi: true',
255263
(WidgetTester tester) async {
256-
final html.Element input =
264+
final web.Element input =
257265
plugin.createInputElement('any', 'something', multiple: true);
258266

259-
expect(input.attributes, containsPair('accept', 'any'));
260-
expect(input.attributes, containsPair('capture', 'something'));
261-
expect(input.attributes, contains('multiple'));
267+
expect(input.getAttribute('accept'), 'any');
268+
expect(input.getAttribute('capture'), 'something');
269+
expect(input.hasAttribute('multiple'), true);
262270
});
263271
});
264272

265273
group('Deprecated methods', () {
266-
late html.FileUploadInputElement mockInput;
274+
late web.HTMLInputElement mockInput;
267275
late ImagePickerPluginTestOverrides overrides;
268276
late ImagePickerPlugin plugin;
269277

270278
setUp(() {
271-
mockInput = html.FileUploadInputElement();
279+
mockInput = web.HTMLInputElement()..type = 'file';
272280
overrides = ImagePickerPluginTestOverrides()
273281
..createInputElement = ((_, __) => mockInput)
274-
..getMultipleFilesFromInput = ((_) => <html.File>[textFile]);
282+
..getMultipleFilesFromInput = ((_) => <web.File>[textFile]);
275283
plugin = ImagePickerPlugin(overrides: overrides);
276284
});
277285

278286
void mockCancel() {
279-
mockInput.dispatchEvent(html.Event('cancel'));
287+
mockInput.dispatchEvent(web.Event('cancel'));
280288
}
281289

282290
void mockChange() {
283-
mockInput.dispatchEvent(html.Event('change'));
291+
mockInput.dispatchEvent(web.Event('change'));
284292
}
285293

286294
group('getImage', () {
@@ -306,7 +314,7 @@ void main() {
306314
expect(
307315
file.lastModified(),
308316
completion(
309-
DateTime.fromMillisecondsSinceEpoch(textFile.lastModified!),
317+
DateTime.fromMillisecondsSinceEpoch(textFile.lastModified),
310318
));
311319
});
312320

@@ -326,7 +334,7 @@ void main() {
326334
testWidgets('can select multiple files', (WidgetTester _) async {
327335
// Override the returned files...
328336
overrides.getMultipleFilesFromInput =
329-
(_) => <html.File>[textFile, secondTextFile];
337+
(_) => <web.File>[textFile, secondTextFile];
330338

331339
// ignore: deprecated_member_use
332340
final Future<List<XFile>> files = plugin.getMultiImage();

packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6-
import 'dart:html' as html;
6+
import 'dart:js_interop';
77
import 'dart:typed_data';
88
import 'dart:ui';
99

1010
import 'package:flutter_test/flutter_test.dart';
1111
import 'package:image_picker_for_web/src/image_resizer.dart';
1212
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
1313
import 'package:integration_test/integration_test.dart';
14+
import 'package:web/helpers.dart';
15+
import 'package:web/web.dart' as web;
1416

1517
//This is a sample 10x10 png image
1618
const String pngFileBase64Contents =
@@ -24,14 +26,13 @@ void main() {
2426
late XFile pngFile;
2527
setUp(() {
2628
imageResizer = ImageResizer();
27-
final html.File pngHtmlFile =
28-
_base64ToFile(pngFileBase64Contents, 'pngImage.png');
29-
pngFile = XFile(html.Url.createObjectUrl(pngHtmlFile),
30-
name: pngHtmlFile.name, mimeType: pngHtmlFile.type);
29+
final web.Blob pngHtmlFile = _base64ToBlob(pngFileBase64Contents);
30+
pngFile = XFile(web.URL.createObjectURL(pngHtmlFile),
31+
name: 'pngImage.png', mimeType: 'image/png');
3132
});
3233

3334
testWidgets('image is loaded correctly ', (WidgetTester tester) async {
34-
final html.ImageElement imageElement =
35+
final web.HTMLImageElement imageElement =
3536
await imageResizer.loadImage(pngFile.path);
3637
expect(imageElement.width, 10);
3738
expect(imageElement.height, 10);
@@ -40,9 +41,9 @@ void main() {
4041
testWidgets(
4142
"canvas is loaded with image's width and height when max width and max height are null",
4243
(WidgetTester widgetTester) async {
43-
final html.ImageElement imageElement =
44+
final web.HTMLImageElement imageElement =
4445
await imageResizer.loadImage(pngFile.path);
45-
final html.CanvasElement canvas =
46+
final web.HTMLCanvasElement canvas =
4647
imageResizer.resizeImageElement(imageElement, null, null);
4748
expect(canvas.width, imageElement.width);
4849
expect(canvas.height, imageElement.height);
@@ -51,19 +52,19 @@ void main() {
5152
testWidgets(
5253
'canvas size is scaled when max width and max height are not null',
5354
(WidgetTester widgetTester) async {
54-
final html.ImageElement imageElement =
55+
final web.HTMLImageElement imageElement =
5556
await imageResizer.loadImage(pngFile.path);
56-
final html.CanvasElement canvas =
57+
final web.HTMLCanvasElement canvas =
5758
imageResizer.resizeImageElement(imageElement, 8, 8);
5859
expect(canvas.width, 8);
5960
expect(canvas.height, 8);
6061
});
6162

6263
testWidgets('resized image is returned after converting canvas to file',
6364
(WidgetTester widgetTester) async {
64-
final html.ImageElement imageElement =
65+
final web.HTMLImageElement imageElement =
6566
await imageResizer.loadImage(pngFile.path);
66-
final html.CanvasElement canvas =
67+
final web.HTMLCanvasElement canvas =
6768
imageResizer.resizeImageElement(imageElement, null, null);
6869
final XFile resizedImage =
6970
await imageResizer.writeCanvasToFile(pngFile, canvas, null);
@@ -112,19 +113,21 @@ void main() {
112113

113114
Future<Size> _getImageSize(XFile file) async {
114115
final Completer<Size> completer = Completer<Size>();
115-
final html.ImageElement image = html.ImageElement(src: file.path);
116-
image.onLoad.listen((html.Event event) {
117-
completer.complete(Size(image.width!.toDouble(), image.height!.toDouble()));
118-
});
119-
image.onError.listen((html.Event event) {
120-
completer.complete(Size.zero);
121-
});
116+
final web.HTMLImageElement image = web.HTMLImageElement();
117+
image
118+
..onLoad.listen((web.Event event) {
119+
completer.complete(Size(image.width.toDouble(), image.height.toDouble()));
120+
})
121+
..onError.listen((web.Event event) {
122+
completer.complete(Size.zero);
123+
})
124+
..src = file.path;
122125
return completer.future;
123126
}
124127

125-
html.File _base64ToFile(String data, String fileName) {
128+
web.Blob _base64ToBlob(String data) {
126129
final List<String> arr = data.split(',');
127-
final String bstr = html.window.atob(arr[1]);
130+
final String bstr = web.window.atob(arr[1]);
128131
int n = bstr.length;
129132
final Uint8List u8arr = Uint8List(n);
130133

@@ -133,5 +136,5 @@ html.File _base64ToFile(String data, String fileName) {
133136
n--;
134137
}
135138

136-
return html.File(<Uint8List>[u8arr], fileName);
139+
return Blob(<JSUint8Array>[u8arr.toJS].toJS);
137140
}

0 commit comments

Comments
 (0)