Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration file_picker to WASM support #212

Closed
amrgetment opened this issue Mar 18, 2024 · 14 comments
Closed

Migration file_picker to WASM support #212

amrgetment opened this issue Mar 18, 2024 · 14 comments

Comments

@amrgetment
Copy link

I want to compile my app to WASM but I found that I need to migrate several libraries to WASM

Context: The unavailable library 'dart:html' is imported through these packages:

    web_plugin_registrant.dart => package:file_picker => dart:html
    web_plugin_registrant.dart => package:flutter_keyboard_visibility_web => dart:html
    web_plugin_registrant.dart => package:fluttertoast => dart:html
    web_plugin_registrant.dart => package:permission_handler_html => dart:html
    web_plugin_registrant.dart => package:share_plus => dart:html

So I started by migrating file_picker library
I don't know if here is the right place for asking support
I made the initial migration but I got two cases which I don't know how to migrate

Case 1:
_target.children.clear();

Case 2:
await reader.onLoad.first;

I wonder how to migrate these two cases
My commit after the initial attempt to migration
amrgetment/flutter_file_picker@1ded925

And I wonder if here is the right place to ask about migration support?

@srujzs
Copy link
Contributor

srujzs commented Apr 2, 2024

And I wonder if here is the right place to ask about migration support?

Sure, one of the goals is to help migrate away from dart:html.

_target.children.clear();

There's some wrapping with lists going on with children, but ultimately the implementation points to https://github.com/dart-lang/sdk/blob/c5f876b7aaca65c6bdc6d718cf18d0ee9c335afb/sdk/lib/html/dart2js/html_dart2js.dart#L23276. So the equivalent would be:

var firstChild = _target.firstChild;
while (firstChild != null) {
  _target.removeChild(firstChild);
  firstChild = _target.firstChild;
}

This may not be the fastest (might be faster to remove the children starting from the end) or most idiomatic way, but it's the identical equivalent.

await reader.onLoad.first;

Use the events helpers in the helpers folder e.g.

await EventStreamProviders.loadEvent.forTarget(reader).first;

There's events.dart which tries to keep the same syntax as dart:html but doesn't have all the events added there yet. Feel free to add a missing member in a PR (like with onLoad).

@amrgetment
Copy link
Author

amrgetment commented Apr 3, 2024

I will try it on the weekend, thanks a lot
I will close this issue and I will open it if it doesn’t work later

@amrgetment
Copy link
Author

I still need support I tried to build with WASM but I got the following error

../lib/_internal/file_picker_web.dart:142:66: Error: Function converted via 'toJS' contains invalid
types in its function signature: 'void Function(*dynamic*)'.
Use one of these valid types instead: JS types from 'dart:js_interop', ExternalDartReference, void,
bool, num, double, int, String, extension types that erase to one of these types, '@staticInterop'
types, 'dart:html' types when compiling to JS, or a type parameter that is a subtype of a valid
non-primitive type.
      window.removeEventListener('focus', cancelledEventListener.toJS);
                                                                 ^
../lib/_internal/file_picker_web.dart:156:64: Error: Function converted via 'toJS' contains invalid
types in its function signature: 'void Function(*dynamic*)'.
Use one of these valid types instead: JS types from 'dart:js_interop', ExternalDartReference, void,
bool, num, double, int, String, extension types that erase to one of these types, '@staticInterop'
types, 'dart:html' types when compiling to JS, or a type parameter that is a subtype of a valid
non-primitive type.
    uploadInput.addEventListener('change', changeEventListener.toJS);
                                                               ^
../lib/_internal/file_picker_web.dart:157:67: Error: Function converted via 'toJS' contains invalid
types in its function signature: 'void Function(*dynamic*)'.
Use one of these valid types instead: JS types from 'dart:js_interop', ExternalDartReference, void,
bool, num, double, int, String, extension types that erase to one of these types, '@staticInterop'
types, 'dart:html' types when compiling to JS, or a type parameter that is a subtype of a valid
non-primitive type.
    uploadInput.addEventListener('cancel', cancelledEventListener.toJS);
                                                                  ^
../lib/_internal/file_picker_web.dart:160:61: Error: Function converted via 'toJS' contains invalid
types in its function signature: 'void Function(*dynamic*)'.
Use one of these valid types instead: JS types from 'dart:js_interop', ExternalDartReference, void,
bool, num, double, int, String, extension types that erase to one of these types, '@staticInterop'
types, 'dart:html' types when compiling to JS, or a type parameter that is a subtype of a valid
non-primitive type.
    window.addEventListener('focus', cancelledEventListener.toJS);

My migration

image

@amrgetment amrgetment reopened this Apr 6, 2024
@ykmnkmi
Copy link

ykmnkmi commented Apr 6, 2024

With the new interop, you can't use the dynamic type. Add the Event type to the e argument of the changeEventListener function:

void changeEventListener(Event e) async {
  // ...
}

@amrgetment
Copy link
Author

Thanks builds fine now 😍

@amrgetment
Copy link
Author

amrgetment commented Apr 6, 2024

I tried to run the app for testing and I got the following error:
Error: TypeError: Instance of 'NativeByteBuffer': type 'NativeByteBuffer' is not a subtype of type 'Uint8List?'
for this code
addPickedFile(file, reader.result as Uint8List?, null, null);

so how to convert NativeByteBuffer to Uint8List

@amrgetment amrgetment reopened this Apr 6, 2024
@amrgetment
Copy link
Author

I used this conversion and it works fine now

          ByteBuffer? byteBuffer = reader.result as ByteBuffer?;
          addPickedFile(file, byteBuffer?.asUint8List(), null, null);

@amrgetment
Copy link
Author

My full PR for the reference
miguelpruivo/flutter_file_picker#1481

@srujzs
Copy link
Contributor

srujzs commented Apr 8, 2024

Sidecasting an interop type to a Dart type may not work on both JS and Wasm.

reader.result as ByteBuffer?

This will definitely throw on dart2wasm. Instead, downcast to another interop type, and convert as needed. In this case, try:

ByteBuffer? byteBuffer = (reader.result as JSArrayBuffer).toDart;

I also see a reader.result as String? in your PR. Prefer:

String? result = (reader.result as JSString).toDart;

See https://dart.dev/interop/js-interop/js-types#compatibility-type-checks-and-casts for more details.

@amrgetment
Copy link
Author

ok, I will update it, thanks a lot

@amrgetment
Copy link
Author

I wonder why byteBuffer is not nullable here?
image

@amrgetment amrgetment reopened this Apr 8, 2024
@srujzs
Copy link
Contributor

srujzs commented Apr 8, 2024

Presumably, analysis is smart enough to know that its value is non-nullable? This also gives me a hint:

void main() {
  int? x = 5;
  x?.isEven;
}

If you don't know if reader.result is nullable or not, you can downcast to the nullable type instead:

ByteBuffer? byteBuffer = (reader.result as JSArrayBuffer?)?.toDart;

@amrgetment
Copy link
Author

Thanks
Works fine now, I wonder how to run Flutter with WASM in debugging mode? is it by --web-renderer skwasm

@srujzs
Copy link
Contributor

srujzs commented Apr 9, 2024

dart2wasm doesn't have a debugging mode yet unfortunately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants