javascriptdartdart2js

How can I convert a Dart Future to a Javascript Promise?


The dart code I compile is:

import 'dart:js';
import 'package:dio/dio.dart';

final Dio _dio = Dio();

class LoginResponse extends GenericResponse {
  String? accessToken;
  String? refreshToken;

  LoginResponse(statusCode, this.accessToken, this.refreshToken)
      : super(statusCode);
}


Future<LoginResponse> login(String phoneNumber, String password, String otp) async {
    Response response = await _dio.post(
      "https://example.com/token/obtain/",
      data: {'phone_number': phoneNumber, 'password': password, 'otp': otp},
      options: Options(validateStatus: (status) => status! < 500),
    );
    return LoginResponse(response.statusCode, response.data['access'], response.data['refresh']);
  }

main() => context['loginFromDart'] = login;

I wish to access LoginResponse.accessToken and LoginResponse.refreshToken from the browser console. But still didn't find a way to do it. enter image description here


Solution

  • You're looking for the dart:js library.

    You need to basically use allowInterop and the @JS() notation.

    Setup

    The Promise class

    You define a custom Promise class using the @JS annotation. This is necessary to create a promise instance that can be used from JavaScript to interact with Dart asynchronous code:

    @JS('Promise')
    class Promise<T> {
      external Promise(void executor(void resolve(T result), Function reject));
      external Promise then(void onFulfilled(T result), [Function onRejected]);
    }
    

    For your example - LoginClass

    class LoginResponse {
      int userId;
      int id;
    
      LoginResponse(this.userId, this.id);
    }
    

    Dio and request setup

    final _dio = Dio();
    Future<LoginResponse> login() async {
      // _dio.get("URL")
      return LoginResponse(
        response.data['userId'],
        response.data['id'],
      );
    }
    

    Perform login

    The performLogin function wraps the login process in a promise, allowing it to be accessed from JavaScript:

    Promise performLogin() {
      return Promise(js.allowInterop((resolve, reject) {
        login().then((value) {
          final jsonMap = {
            'userId': value.userId,
            'id': value.id,
          };
          final jsonString = jsonEncode(jsonMap);
          resolve(jsonString);
        }).catchError((error) {
          reject(error);
        });
      }));
    }
    

    Binding performLogin to Javascript Window Object:

    You use the setProperty function from the js_util library to bind the performLogin function to the window object. This makes it accessible from the browser console:

    void main() {
      setProperty(window, 'performLogin', js.allowInterop(performLogin));
    }
    

    Calling the code within the Browser console

    You can now use:

    performLogin().then(result => {
    // You need to convert the result to JSON
      const userData = JSON.parse(result);
      console.log('userId: ', userData.userId);
      console.log("ID: ", userData.id);
    
    
    });
    

    Result

    enter image description here

    Putting it all together

    Here's the complete code as a runnable snippet

    import 'dart:html';
    import 'dart:js' as js;
    import 'dart:js_interop';
    import 'package:js/js_util.dart';
    
    import 'package:dio/dio.dart';
    import 'dart:convert';
    
    @JS('Promise')
    class Promise<T> {
      external Promise(void executor(void resolve(T result), Function reject));
      external Promise then(void onFulfilled(T result), [Function onRejected]);
    }
    
    class LoginResponse {
      int userId;
      int id;
    
      LoginResponse(this.userId, this.id);
    }
    
    final _dio = Dio();
    Future<LoginResponse> login() async {
      Response response = await _dio.get(
        "https://jsonplaceholder.typicode.com/posts/1",
        options: Options(validateStatus: (status) => status! < 500),
      );
      return LoginResponse(
        response.data['userId'],
        response.data['id'],
      );
    }
    
    Promise performLogin() {
      return Promise(js.allowInterop((resolve, reject) {
        login().then((value) {
          final jsonMap = {
            'userId': value.userId,
            'id': value.id,
          };
          final jsonString = jsonEncode(jsonMap);
          resolve(jsonString);
        }).catchError((error) {
          reject(error);
        });
      }));
    }
    
    void main() {
      setProperty(window, 'performLogin', js.allowInterop(performLogin));
    }
    
    

    See also