Is there a way to ensure the bindings are initialized between Flutter and Host/ Platform (Android, iOS)? I want to do this because I want to invoke methods from host to dart.
For example, in Android (java), I create a flutter engine, register plugins, and create a method channel, and launch the application:
FlutterEngine flutterEngine = new FlutterEngine(context, null);
// Remember to register plugins
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
DartExecutor executor = flutterEngine.getDartExecutor();
methodChannel = new MethodChannel(executor.getBinaryMessenger(), "my background method channel");
methodChannel.setMethodCallHandler(this);
// Launch app
DartExecutor.DartEntrypoint appEntrypoint = DartExecutor.DartEntrypoint.createDefault();
executor.executeDartEntrypoint(appEntrypoint);
I'd like to use methodChannel.invokeMethod, but the application might not be ready for me yet, even though in appEntrypoint, the same MethodChannel is created on the dart side. If I call methodChannel.invokeMethod too early, I get:
2021-09-14 16:41:01.102 30103-30425/com.example.app E/flutter:
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception:
MissingPluginException
(No implementation found for method MethodName on channel com.example.app.package)
A solution would be to ask the Dart side to call a PlatformMethod when it's ready initializing, which would inform me that it's ready to receive messages. But I would prefer to avoid having to set up 2 way communication if I only need to send one message to the Dart side.
I have a feeling it's not possible. If so, could someone still explain why the binding has to be awaited from the Dart side? Something to do with no listeners available on the Platform side?
The methods were actually being called.
It was working. This was a bad idea, but the actual methods were being called: the 2 Method Channels on dart side and 2 method channels on Platform side were calling each other just fine. They all had the same channel name. Probably not a good idea for complexity reasons (see below), but it worked. 😅
My specific issue:
The reason I was getting No implementation found for method MethodName was exactly because I had two MethodChannels on the dart side, and two on Platform side. I was recreating the same isolate, so I had 2 Flutter applications running simultaneously. When one Isolate called a dart method I did not implement on on Method Call Handler on Platform side, I got this error.
Lesson learnt
If you have separate components on your app (Android components: Activity, Service, Broadcast Receiver or Dart Isolates):
Don't re-create the main application entrypoint. Create a new separate DartEntrypoint.
Use unique channel names for the MethodChannel.
Related
Please explain to me what is the difference between them. Can they be used together or only separately?
FlutterError.onError is a method that allows you to hook into the Flutter framework's error-handling mechanism, so you can receive callbacks when an error occurs during the build, layout, or painting phases.
runZonedGuarded is a utility function that allows you to run a block of code in a new zone with a custom error handler. The onError parameter is the error handler that will be called if an error occurs within the zone.
So, in summary:
FlutterError.onError is specific to the Flutter framework and is used
to handle errors during the build, layout, and painting phases.
runZonedGuarded.onError is more general and allows you to run a block
of code in a new zone with a custom error handler, which can be used
for a wider range of error-handling needs.
Yes, FlutterError.onError and runZonedGuarded.onError can be used together in a Flutter application.
For example, you can use FlutterError.onError to handle errors that occur during the build, layout, or painting phases, and use runZonedGuarded.onError to handle errors that occur within a specific block of code.
In this way, you can have multiple error handlers in your Flutter application to handle different types of errors in different parts of the code.
I am currently developing a flutter web app, and would like to implement a feature to broadcast json data to any listeners/subscribers.
To give more detail:
The web app has a flutter front end server running on localhost. A user will be able to fetch some JSON data from an external backend server using a simple GET request.
Once the app has loaded the JSON data, it will need to broadcast it down an SSE stream to any listeners. This listener will be a seperate flutter application.
Now, I understand that there a few problems with this.
Firstly, Flutter web doesn't support dart:io... Fine - I have managed to get round this by using the universal_io dart package instead of dart:io. The api is the same, and after solving a few CORS errors, works a charm for the GET requests in the first bullet above. This means that the json data can be successfully loaded into the Flutter app.
(universal_io dart package -> https://pub.dev/packages/universal_io).
The next issue is with SSE. Part one of this is that there is not much documentation on implementing an SSE server in Flutter - only a client that can listen. However, I have managed to find a solution in the sse dart package example app...
import 'package:shelf/shelf_io.dart' as io;
import 'package:sse/server/sse_handler.dart';
/// A basic server which sets up an SSE handler.
///
/// When a client connnects it will send a simple message and print the
/// response.
void main() async {
var handler = SseHandler(Uri.parse('/sseHandler'));
await io.serve(handler.handler, 'localhost', 0);
var connections = handler.connections;
while (await connections.hasNext) {
var connection = await connections.next;
connection.sink.add('foo');
connection.stream.listen(print);
}
}
(sse dart package -> https://pub.dev/packages/sse)
(continued) Usually, this would be a fine solution, and it could probably be modified a little for implementation within a flutter app. However, the shelf_io.dart package, used to spin up the server, depends on the dart:io package, which doesn't work with Flutter Web.
The other thought is - why do we need to set up a new server connection (with io.server(...)) when the flutter web app is already running on its own localhost server? This begs the question: Is there a way to add this sseHandler endpoint onto the localhost server that the Flutter Web app is running on?
All in all, what i'm trying to ask is:
Is there a way for a Flutter Web App to send SSE messages to a collection of listeners?
At the moment I've hit a bit of a deadend with research on this so if anyone had any bright ideas on a potential solution then the help would be greatly appreciated. Thanks :)
Side Note: Obviously this is something that could very easily be done with Java, J2Html and so on, but wheres the fun in giving up just like that... On the other hand, maybe flutter web is still just too new to support stuff like this?
Within our flutter app we are doing some background processing. For that, we need to create new flutter isolate in our native code so we can run code when activity is not open, based on this guide: https://medium.com/#chetan882777/initiating-calls-to-dart-from-the-native-side-in-the-background-with-flutter-plugin-7d46aed32c47
Now, to not duplicate code and to not cause any concurrency issues, we would also like to have access to that background processing isolate from main flutter UI isolate. That way we can begin processing from both native code and from flutter UI code.
However, there does not seem to be a way for native to pass this Isolate / ControlPort to the main UI side so it can communicate with it. Is there a way I can achieve this (communicate to the same Isolate from both native and UI side)?
From what I see, only way to do this would require native to be the broker between the two sides (send the task to native and then native sends it back to the other side), but it seems like a lot of hassle for one flutter talking to another flutter.
Solution to this is the IsolateNameServer.
One side can call IsolateNameServer.registerPortWithName() and other side can then send messages to that port via IsolateNameServer.lookupPortByName()
I'm working on a Flutter app with some native kotlin code. It's straightforward to call native methods from Flutter through the MethodChannel, and vice-versa (from MainActivity).
However, I also want a way to trigger Flutter code triggered from a native Broadcast receiver, where I don't have the original MethodChannel. The problem is, I'm not sure how to create a FlutterEngine with the right arguments to get access to the same MethodChannel.
Is there a way to construct the same FlutterEngine that can be used to call back into Flutter code? What I want to do:
var flutterEngine = FlutterEngine(XXX)
var flutterChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, FLUTTER_CHANNEL)
flutterChannel.invokeMethod(...)
I'm not sure what XXX should be here to get the same Channel I have in MainActivity. If there's some info I need to pass from MainActivity, I could use intents to pass it to my broadcast receiver, but it's not clear what is needed.
Flutter Engine and Channels work with named scopes, if you uses the same engine name end channel is the same channel in Flutter side.
In Flutter, there are three types of platform channels, and I want to know about the difference between them.
These channels are used to communicate between native code (plugins or native code inside of your project) and the Flutter framework.
MethodChannel
A MethodChannel is used for "communicating with platform plugins using asynchronous method calls". This means that you use this channel to invoke methods on the native side and can return back a value and vise versa.
You can e.g. call a method that retrieves the device name this way.
EventChannel
An EventChannel is used to stream data. This results in having a Stream on the Dart side of things and being able to feed that stream from the native side.
This is useful if you want to send data every time a particular event occurs, e.g. when the wifi connection of a device changes.
BasicMessageChannel
This is probably not something you will want to use. BasicMessageChannel is used to encode and decode messages using a specified codec.
An example of this would be working with JSON or binary data. It is just a simpler version because your data has a clear type (codec) and you will not send multiple parameters etc.
Here is a link to a good explanation for you https://medium.com/flutter-io/flutter-platform-channels-ce7f540a104e
Basically there are two main types:
Method Channels: designed for invoking named pieces of code across Dart and Java/Kotlin or Objective-C/Swift. (From flutter to the platform)
Event Channels: specialized platform channel intended for the use case of exposing platform events to Flutter as a Dart stream. (From the platform to flutter)
#creativecreatorormaybenot answer clears the things, let me add more to this.
Method Channel
This is more like RPC call. You invoke a method from your Flutter app to the native code, the native code does something and finally responds with a success or error. This call could be to get the current battery status, network information or temperature data. Once the native side has responded, it can no longer send more information until the next call.
Method Channel provides platform communication using asynchronous method calls.
Note:- If desired, method calls can also be sent in the reverse
direction, with the platform acting as client to methods implemented
in Dart.
Event Channel
This is more like reactive programming where platform communication using asynchronous event streams. These events could be anything that you need streamed to your Flutter application.Streaming data from the native code to Flutter app like continuously updating BLE or WiFi scanning results, accelerometer and gyro, or even periodic status updates from intensive data collection.
Basic Message Channel
It provides basic messaging services similar to BinaryMessages, but with pluggable message codecs in support of sending strings or semi-structured messages. Messages are encoded into binary before being sent, and binary messages received are decoded into Dart values. The MessageCodec used must be compatible with the one used by the platform plugin.