How to convert a Dart Future to a Javascript Promise for flutter webview

338 Views Asked by At

I have used webview_flutter to load my site url, and used a JavaScriptChannel for two way communication between javascript and dart, as i want some native result from Android/IOS platform to javascript.

In Flutter

controller = WebViewController()
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..addJavaScriptChannel('FlutterChannel',
      onMessageReceived: (JavaScriptMessage message) async {
    
        //Here i am doing some operation and returning a result as
        controller.runJavaScriptReturningResult('window.function($result)');
    
  })
  ..loadRequest(
    Uri.parse(
        'https://demo.com/')
  );

In JavaScript to interact with flutter channel-

var params = {'key':'alternate_preference','value': ''}; 
var data = { plugin: 'SharedPref', method: 'fetch', params: [params] };
FlutterChannel.postMessage(JSON.stringify(data))

To capture result in javascript-

function flutterResponseFunc(flutterResult) {
   console.log(flutterResult);
}
window.function= flutterResponseFunc;

How can i capture the result in callback of FlutterChannel.postMessage itself instead of creating a different function for result in js. Like Promise.then function in javascript.

enter image description here

I have gone through this answer

But it seems like a platform specific(i.e. for web platform only), as i can not use dart:js_interop & dart:html libs for Android/IOS platform. Any help will be appreciated. Thanks !!

2

There are 2 best solutions below

3
Furkan Bora On

you don't need to translate the function between languages. You can run simple codes through flutter. I left an example from my own use for easier understanding.

..addJavaScriptChannel(
    'consolePrint',
    onMessageReceived: (value) {
      final fileName = DateTime.now().toString().replaceAll(':', '-');
      final Map linkList = jsonDecode(value.message.toString());

      int totalCalculate(Map returnValue) {
        if (returnValue.containsValue(null)) {
          returnValue.removeWhere((key, value) => value == null);
          return returnValue.length;
        }
        return returnValue.length;
      }

      ProgressBar.total = 0;
      ProgressBar.progressValue.value = 0;
      ProgressBar.total = totalCalculate(linkList);
      linkList.forEach((key, value) {
        if (value != null) {
          downloadJpg(
              index: int.parse(key),
              url: value,
              fileNmae: fileName,
              total: linkList.length);
        }
      });
    },
  );

Run and create a function from flutter. Then return the result to a channel. consolePrint is the name of my channel here

void getLinkMap(WebViewController controller) async {
  try {
    await controller.runJavaScript('''
                  function getImages() {
                    var json = {};
                    var liList = document.querySelectorAll("body > div.main-content > div.ng-scope > div.content > div:nth-child(3) > div.modal-content.mega-image > section > ul > li")
                    liList.forEach(function list(value, index) {
                      var img = value.querySelector('img');
                      var hdImage = img.getAttribute("hd-image");
                      json[index] = hdImage;
                    });
                    consolePrint.postMessage(JSON.stringify(json));
                  };
                  window.onload = getImages();
              ''');
  } catch (e) {
    log('error code: $e',
        time: DateTime.now(), name: 'func:getLinkMap');
  }
}
0
tynn On

Considering the signature of the addJavaScriptChannel function it's not possible to provide a result to postMessage().

Future<void> addJavaScriptChannel(
    String name,
    {required void onMessageReceived(
        JavaScriptMessage
    )}
) 

The onMessageReceived doesn't return any value after invocation and JavaScriptMessage doesn't provide a callback to invoke with data.

So any aproach you could take would require you to build a custom solution with non-enforcable contracts within JS. As this is complex and error prone, it might be easier to just handle the result as you're doing already.