I want to call a native method from my flutter app but I have an issue:
No implementation found for method getApplicationDocumentsDirectory on channel my_channel/name
I launch my Flutter app from an existing native Android Application and I want to use some native code from Flutter.
So I use an Flutter engine cache and I register my native method handler but it doesn’t work:
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"CHANNEL"
).setMethodCallHandler { call, result ->
when (call.method) {
"myMethod" -> {
doMyNativeCode(call, result)
}
else -> {
result.notImplemented()
}
}
}
}
I call this code from Application.onCreate() when I create flutter engine and put it into cache.
But my EventChannel's (listening events from native on flutter) work well.
What's wrong with it?
Maybe my experience can help you.
My issue was exception on native side:
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"CHANNEL"
).setMethodCallHandler { call, result ->
when (call.method) {
"myMethod" -> {
// THERE WAS EXCEPTION!!!
}
else -> {
result.notImplemented()
}
}
}
}
Related
I want to send data with intent from native android app to flutter app, So if flutter app is closed below code working fine to fetch intent data in main.dart file. If flutter app is in foreground and i tries to send data from native app to flutter app nothing happens. Is their anything else to need implement for this case?
Native app code to start flutter app
var intent = getPackageManager().getLaunchIntentForPackage("com.flutterapp");
intent.putString( "MapParams", jsonObj.toString())
startActivity(intent)
Flutter app code
class MainActivity: FlutterActivity()
var sharedData="";
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
"share_channel").setMethodCallHandler { call, result ->
if (call.method == "MapParams") {
handleIntent()
result.success(sharedData)
sharedData = ""
}
}
}
private fun handleIntent() {
if (getIntent().hasExtra("MapParams")) {
getIntent().getSerializableExtra("MapParams")?.let { intentData ->
sharedData = intentData.toString()
}
}
}
Main.Dart file
Future<String> getSharedData() async {
return await MethodChannel('share_channel')
.invokeMethod("MapParams") ??
"";
}
I have created a custom MethodChannel "com.example.app/widget" that updates a home screen widget on Android after receiving a Firebase Cloud Message. It runs fine when it is called while the app is in the foreground, but I would also like to call it when a Firebase Cloud Message is received while the app is closed or in the background.
When the app is in the background, it gives me a MissingPluginException error, like below:
E/flutter (28540): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: MissingPluginException(No implementation found for method updateFromFlutter on channel com.example.app/widget)
E/flutter (28540): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:157:7)
... and so on. There are a lot of other threads about MissingPluginException errors that deal with adding a plugin to the registry, but I haven't been able to find any that address custom MethodChannels that are not part of another plugin. Is it possible to add my custom MethodChannel to the registry or do something similar that will result in the Dart code being able to call a method from that channel while in the background?
I have tried using workmanager and android_alarm_manager and they seem to run fine themselves, but they still can't get past this block with my custom channel.
My MainActivity.kt has the method details in it:
class MainActivity: FlutterActivity(), MethodChannel.MethodCallHandler {
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.app/widget")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"updateFromFlutter" -> {
val views = RemoteViews(context.packageName, R.layout.appwidget).apply {
setTextViewText(R.id.text, call.argument("text"))
}
val manager = AppWidgetManager.getInstance(this)
manager.updateAppWidget(call.argument("idNumber"), views)
}
}
}
}
Then in main.dart I call:
Future<void> updateAndroidWidget(String text) async {
WidgetsFlutterBinding.ensureInitialized();
const MethodChannel platform = MethodChannel('com.example.app/widget');
try {
platform.invokeMethod("updateFromFlutter", {
"text": text,
"idNumber": savedPreferences.androidWidgetID
});
} catch (e) {
print("failed: $e");
}
}
I already have Flutter Android Embedding V2 (Flutter Version >= 1.12).
Any help is greatly appreciated.
I am working on a native Android widget in a Flutter App. In which there is refresh button, on click of that I have to call a method in the Flutter code. I am using Flutter Method Channel for the communication and it is working fine when app is in foreground. But it does not work when app is minimised or closed. I get error PlatformException(NO_ACTIVITY, null, null). Below is my code.
Android (AppWidgetProvider)
if (methodChannel == null && context != null) {
FlutterMain.startInitialization(context)
FlutterMain.ensureInitializationComplete(context, arrayOf())
// Instantiate a FlutterEngine.
val engine = FlutterEngine(context.applicationContext)
// Define a DartEntrypoint
val entrypoint: DartEntrypoint = DartEntrypoint.createDefault()
// Execute the DartEntrypoint within the FlutterEngine.
engine.dartExecutor.executeDartEntrypoint(entrypoint)
// Register Plugins when in background. When there
// is already an engine running, this will be ignored (although there will be some
// warnings in the log).
//GeneratedPluginRegistrant.registerWith(engine)
methodChannel = MethodChannel(engine.dartExecutor.binaryMessenger, MainActivity.CHANNEL)
}
methodChannel!!.invokeMethod("fetchNewData", "", object : MethodChannel.Result {
override fun notImplemented() {
Toast.makeText(context, "method not implemented", Toast.LENGTH_SHORT).show()
}
override fun error(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show()
}
override fun success(result: Any?) {
Toast.makeText(context, "success", Toast.LENGTH_SHORT).show()
}
})
Flutter
/// calling in main
static Future<void> attachListeners() async {
WidgetsFlutterBinding.ensureInitialized();
var bloc = new AqiCnDashboardBloc();
_channel.setMethodCallHandler((call) {
switch (call.method) {
case 'fetchNewData':
bloc.getAqiCn(false);
return null;
default:
throw MissingPluginException('notImplemented');
}
});
}
I am collecting information/discussion that redirects us to run flutter engine in background.
void callbackDispatcher() {
WidgetsFlutterBinding.ensureInitialized();
print("Our background job ran!");
}
void main() {
static const MethodChannel _channel = const MethodChannel("channel-name");
Future<void> initialize(final Function callbackDispatcher) async {
final callback = PluginUtilities.getCallbackHandle(callbackDispatcher);
await _channel.invokeMethod('initialize', callback.toRawHandle());
}
}
As stated here How to run Flutter in the background?
When a background job is started by native the Flutter engine is not active. So we are unable to run Dart.
Can Android/iOS starts the Flutter engine in the background?
Yes! We’ll first need to register a Dart callback function which will only be invoked whenever a background job is started by the native code.
This callback function is referred to as a callbackDispatcher.
Also please check out these stackoverflow discussions.
Flutter : Run an app as a background service
How to create a service in Flutter to make an app to run always in background?
How to create a Flutter background service that works also when app closed
Executing Dart in the Background with Flutter Plugins and Geofencing
You may start the Flutter Engine in the background by register a Dart callback function which will only be invoked whenever a background job is started in Flutter.
Try this.
https://medium.com/vrt-digital-studio/flutter-workmanager-81e0cfbd6f6e
The experimental implementation of launchIn throws an error for not implementing within a suspend function. I've filed an issue to see if this behavior is intended.
Error
Suspend function 'getFeed' should be called only from a coroutine or another suspend function
However, because launchIn is the creator of a Coroutine this error does not seem valid.
feedRepository.getFeed().onEach { results ->
when (results.status) {
LOADING -> ...
SUCCESS -> withContext(Dispatchers.Main) {
_feedViewState._feed.value = results.data
}
ERROR -> ...
}
}
.flowOn(Dispatchers.IO)
.launchIn(viewModelScope)
Original implementation
viewModelScope.launch(Dispatchers.IO) {
feedRepository.getFeed().collect { results ->
when (results.status) {
LOADING -> ...
SUCCESS -> withContext(Dispatchers.Main) {
_feedViewState._feed.value = results.data
}
ERROR -> ...
}
}
}
The issue has been resolved.
The problem was that the getFeed method, was implemented with the suspend syntax. suspend is not needed when returning a Flow, because Flow is run declaratively, meaning getFeed defines the code that will be run when called. The code will run when launchIn initiates it rather than being run imperatively when the method is first called by itself.
This concept is defined well in this talk, KotlinConf 2019: Asynchronous Data Streams with Kotlin Flow by Roman Elizarov
Before
suspend fun getFeed() = flow { ... }
After
fun getFeed() = flow { ... }
Basically I have to make a network request using OkHttp in parallel to various addresses. I only care about the result of the first one that succeeds. Can I do this with Flow on Kotlin?
I've been looking around but I'm struggling with getting the requests to run in parallel, the always run in sequence.
The code basically takes a list of addresses and should return the only address that worked or null if none worked.
Thanks.
Edit: I should mention I plan on using this on Android. I can probably do it with RX but wanted to learn Flow. Also trying to limit the libraries I add to the app.
Edit: I have marked an answer as correct however that isn't how I did but it took me very close to how I did it but since I'm new to Flow I have no idea if how I did it is correct though I'm pretty sure it works after my testing.
I have a function that throws NoSuchElementException when not found. It calls searchForIPAsync which is a suspend function that does all the OkHttp work and returns true|false.
#Throws(NoSuchElementException::class)
private suspend fun findWorkingIP(ipsToTest: MutableList<String>): String? = ipsToTest
.asFlow()
.flatMapMerge(ipsToTest.size)
{ impl ->
flow<String?> {
val res = connectionHelper.searchForIPAsync(getURLToTest(impl))
if (res) {
emit(impl)
} else {
}
}
}.first()
Then I call this and catch the exception in case nothing is found:
try {
val ipFound = findWorkingIP(ipsToTest)
Log.w(TAG, "find: Got something " + ipFound);
return ipFound
} catch (ex: NoSuchElementException) {
Log.w(TAG, "find: not found");
}
Although the Flow-based solution in another answer is a close match to what you need, unfortunately as of Kotlin 1.3.2 the Flow implementation has a bug that breaks it. The bug already has a proposed fix so this should be resolved with the next patch release of Kotlin. In the meantime, here's a similar solution that uses async and Channel instead:
suspend fun getShortUrl(urls: List<String>): String = coroutineScope {
val chan = Channel<String?>()
urls.forEach { url ->
launch {
try {
fetchUrl(url)
} catch (e: Exception) {
null
}.also { chan.send(it) }
}
}
try {
(1..urls.size).forEach { _ ->
chan.receive()?.also { return#coroutineScope it }
}
throw Exception("All services failed")
} finally {
coroutineContext[Job]!!.cancelChildren()
}
}