How to receive data back to flutter when calling a method using js.context.callMethod()? How to receive data back to flutter when calling a method using js.context.callMethod()? flutter flutter

How to receive data back to flutter when calling a method using js.context.callMethod()?


When calling callMethod it returns exactly what the javascript function returns. It doesn't return a json encoded String like your code currently implies. Therefore there is no need to decode it. var obj can nearly be treated as a List<Map<String, dynamic>> object. This means you can access say the first object of the list and the key property with js.context.callMethod("read")[0]['key'] in your dart code. You can remove all the json decoding and casting in your code and replace it with a function that uses this method of accessing the returned data to convert it to an actual List<Map<String, dynamic>> yourself.

Example getAttendees modification:

Future<void> getAttendees() async {  List<Map<String, dynamic>> toReturn = List();  js.JsArray obj = js.context.callMethod("read");  for(js.JsObject eachObj in obj) {    //For every object in the array, convert it to a `Map` and add to toReturn    toReturn.add({            'key': eachObj['key'],      'user': eachObj['user'],    });  }  setState(() {    data = toReturn;  });}

If you for some reason find the need to use json, you would need to encode on the javascript side, though I don't see a reason to do this as it just adds complexity.

When displaying the data in the build function data.toString()[index] will first convert the javascript output to a String like this: "[{key: key}, {key: key}, ...]" and then find the index of the String, not the List object, which is likely what you intend. For this you use the index first, data[index] and then add the field you're looking for data[index]['key'] as data is a List of Maps.

Unrelated directly to the problem you're having, but some general flutter advice, is to use a FutureBuilder instead of your current method of showing data when the async function completes. Your method nearly does what a FutureBuilder would, just a bit less efficiently and in a less readable way.

EDIT:To handle read as a Promise:

First you have to add js: ^0.6.2 to your pubspec.yaml dependencies.

Then this must be added somewhere in your code:

@JS()library script.js;import 'package:js/js.dart';import 'dart:js_util';@JS()external dynamic read();

js.JsArray obj = js.context.callMethod("read"); Should be modified to just be await promiseToFuture(read()). The whole getAttendees should be modified to:

Future<void> getAttendees() async {  List<Map<String, dynamic>> toReturn = List();  dynamic obj = await promiseToFuture(read());  for(dynamic eachObj in obj) {    //For every object in the array, convert it to a `Map` and add to toReturn    toReturn.add({            'key': eachObj.key,      'user': eachObj.user,    });  }  setState(() {    data = toReturn;  });}


Since Christopher Moore gave a generalized solution to my problem, I will post an exact implementation I used.

Database Structure -

database

JS Code -

const dbRef = firebase.database().ref();var userList = [];async function read() {  await dbRef.once("value", function (snapshot) {    userList = [];    snapshot.forEach(function (childSnapshot) {      var key = childSnapshot.key;      var user = childSnapshot.val();      userList.push({ key, user });    });    //console.log(userList);  });  return userList;}

JS-Dart Interop -

@JS()library js_interop;import 'package:js/js.dart';import 'package:js/js_util.dart';@JS()external dynamic read();class FBOps {  final List<Map<String, dynamic>> toReturn = [];  Map getUserDetails(objMap) {    final Map<String, dynamic> temp = {};    temp['name'] = objMap.user.name;    temp['status'] = objMap.user.status;    return temp;  }  Future<List> getList() async {    dynamic obj = await promiseToFuture(read());    for (dynamic eachObj in obj) {      toReturn.add({        'key': eachObj.key,        'user': getUserDetails(eachObj),      });    }    return toReturn;  }}

Getting the Map from interop -

List<Map> data;Future<void> getAttendees() async {  await FBOps().getList().then((value) => data = value);  //print(data);}