Search code examples
dartdart-js-interop

How to migrate Blob handling from dart:js to package:web in Flutter Web?


I'm currently transitioning my Flutter Web app to leverage JS functions via the package:web instead of dart:js and similar libraries. However, I've hit a roadblock where the JSArray<JSAny> type isn't resolving correctly. Below is the complete code snippet:

@JS()
library image;

import 'package:flutter/services.dart';
import 'package:web/web.dart';
import 'dart:js_interop';
import 'dart:convert';

@JS('setImage')
external JSPromise _setImage(Blob data); // defined in .js file

void setImage(String data) {
  final blob = Blob(
    // JSArray<JSAny> is required here.
    JsUint8List.from(Uint8List.fromList(base64Decode(data))),
    BlobPropertyBag(type: 'image/png'),
  );
  _setImage(blob);
}

extension type JsUint8List._(Uint8List _) implements JSArray<JSAny> {
  JsUint8List.from(this._);
}

The runtime error I'm encountering is:

Error: The representation type 'Uint8List' of extension type 'JsUint8List' must be either a subtype of the representation type 'JSArray/*1*/<Object?>' of the implemented extension type 'JSArray/*2*/<JSAny>' or a subtype of 'JSArray/*2*/<JSAny>' itself.

Could someone provide guidance on how to properly convert between Dart's Uint8List and Blob using the new package:web?

I attempted to create a Blob using package:js/js.dart, among others.

@JS()
library image;

// ignore: depend_on_referenced_packages
import 'dart:convert';

import 'package:flutter/services.dart';
// ignore: depend_on_referenced_packages
import 'package:js/js.dart';
// ignore: avoid_web_libraries_in_flutter
import 'dart:js_util';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html' as html;

@JS('setImage')
external _setImage(html.Blob data);

void setImage(String data) {
  final blob = html.Blob([Uint8List.fromList(base64Decode(data))], 'image/png');
  _setImage(blob);
}

The code has executed successfully. However, I aim to implement it using the new package:web. I referred to this page and to another source (specifically about extension types).


Solution

  • You can define the Blob class in Dart for JavaScript interop using the @JS annotation. Below is an example where I've added two arguments. You can refer to the Blob documentation and complete them if necessary. However, this should be sufficient for basic use cases:

    import 'dart:typed_data';
    import 'dart:js_interop';
    
    @JS("Blob")
    extension type Blob._(JSObject _) implements JSObject {
      external factory Blob(JSArray<JSArrayBuffer> blobParts, JSObject? options);
      
      factory Blob.fromBytes(List<int> bytes) {
        final data = Uint8List.fromList(bytes).buffer.toJS;
        return Blob([data].toJS, null);
      }
      
      external JSArrayBuffer? get blobParts;
      external JSObject? get options;
    }