How to invoke Flutter MethodChannel from Android and wait for the result to finish

585 Views Asked by At

On Android side, I need to get a data that will be returned by Flutter side MethodChnnel.

As we know, on Flutter side, we can easily user Dart async/await() the result from Native side. But on Android side, it seems that can't use runBlocking or similar ways to wait for the callback to finish and get the returned data. Using runblocking will cause a UI frozen .

Is that possible to acheive someting like this? Note: runBlcking in the following code doesn't work and causes a frozen

fun getFlutterData(): String? {
    var res: String? = null
    runBlocking {
        res = suspendCoroutine {
            channel!!.invokeMethod(flutterMethod, params, object : MethodChannel.Result {
                 override fun success(result: Any?) {
                    println("callFlutterMethod success: $result")
                    ** it.resume(res)**
                }

                override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
                    println("callFlutterMethod error: $errorCode, $errorMessage,$errorDetails")
                }

                override fun notImplemented() {
                }
            })

        }
    }
    
    
   ** return res **// get the result and assign to other variable

}
1

There are 1 best solutions below

1
Jan Homola On

You should use kotlin coroutines.

When you use runBlocking, you freeze current thread, in you case main (ui) thread. Instead you should wait for callback in background.

suspend fun invokeMethodSuspend(
    method: String,
    arguments: Any? = null,
): Any? {
    return suspendCoroutine { continuation ->
        runBlocking(Dispatchers.Main) {
            methodChannel.invokeMethod(
                method,
                arguments,
                object : MethodChannel.Result {
                    override fun success(result: Any?) {
                        continuation.resume(result)
                    }

                    override fun error(
                        errorCode: String,
                        errorMessage: String?,
                        errorDetails: Any?
                    ) {
                     continuation.resumeWithException(Exception(errorMessage))
                    }

                    override fun notImplemented() {
                        continuation
                            .resumeWithException(NotImplementedError("Method channel method $method not implemented"))
                    }
                },
            )
        }
    }
}

and then you can call the function for example

fun onButtonTap() {
  CoroutineScope(Dispatchers.IO).launch {
    try {
        val data = methodChannel.invokeMethodSuspend("getData")
        //handle method channel success
    } catch (e: Exception) {
        //handle method channel exception
    }
  }
}

I recommend you to learn more about kotlin coroutines and coroutine scope.

Note, that you must run methodChannel.invokeMethod on main thread.