skip to Main Content

I have an issue where I need to generate rather long URIs that follow the format of this code:

Uri _encodeDataUrl(String base64Data, String mimeType) =>
    Uri.parse('data:$mimeType;base64,$base64Data');

I am passing large(ish) amounts of data, so when I parse the Uri, the UI ends up hanging because of Dart’s single-threaded model (see web performance panel). I can’t use compute as it’s not supported on Flutter Web.

Performance debugging web

I understand that web workers could be a solution, but I’m unsure how I would get my desired Uri class and the end of the day (end goal is to interface with package just_audio)

Is there a way I could leverage web workers? Or any other way to prevent the UI from hanging while it processes this data?

2

Answers


  1. In Flutter Web, if you are parsing a large base64 encoded data URL using Uri.parse, and this is causing the UI to hang, this likely means that the operation is heavy and taking too long on the main thread. While the compute function is generally a good solution to offload expensive computation to a separate isolate, it’s not available on Flutter Web because Web does not support isolates in the same way as Flutter for mobile or desktop platforms.

    To avoid blocking the main thread, you can use a Future to ensure the parsing operation is performed asynchronously. Unfortunately, Uri.parse itself is synchronous and cannot be made asynchronous. But you can mimic the behavior by delaying the execution using Future.delayed. Though this won’t make the operation faster, it will allow the UI thread to breathe.

    Here’s an example of how you might do this using Future:

    Future<Uri> _encodeDataUrl(String base64Data, String mimeType) async {
      return Future<Uri>.delayed(Duration.zero, () {
        return Uri.parse('data:$mimeType;base64,$base64Data');
      });
    }
    

    Using this async function, you can then call it with await in your widget and use a FutureBuilder to show a loading indicator until the parsing is complete. Here’s an example:

    FutureBuilder<Uri>(
      future: _encodeDataUrl(myBase64Data, myMimeType),
      builder: (BuildContext context, AsyncSnapshot<Uri> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator(); // or some other loading indicator
        } else if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        } else {
          return YourWidgetUsingTheUri(snapshot.data); // Replace this with the widget that uses the Uri
        }
      },
    )
    

    However, please note that this doesn’t actually make the operation itself faster or more efficient – it simply ensures that the UI remains responsive during the operation.

    Login or Signup to reply.
  2. To keep your Web UI responsive while running a CPU-bound workload, you have no choice other than have your workload run in a Web worker to release your UI thread.

    In your case, CPU is busy decoding the base 64 data. I’ve tried decoding the base 64 data in a dedicated Web Worker using Squadron (code available here https://github.com/d-markey/squadron_sample/tree/main/lib/src/data-uri). Unfortunately it won’t help because Dart re-encodes data when constructing a DataUri with decoded bytes.

    I was wondering: what are you doing with the Uri after it’s decoded? Maybe there’s another way using the decoded bytes directly (which can be done in a Web Worker leveraging Squadron).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search