Reputation: 189
I'm currently learning Flutter and specially about communicating with the Device itself.
Currently I have set up an MethodChannel, where I receive a List of Strings as result, but now I would like to change it to receiving these string on a stream through an EventChannel.
I know that I still have to rely on the MethodChannel to trigger methods on android side, which then send data (in my case Strings) via EventChannel back to flutter. But I really don't know how to get the EventChannel to send just a few streams (maybe with a little delay in between them ..), since every tutorial about the EventChannel I've found was more confusing than helpful to me :(
Here is the code I currently have for getting something via EventChannel:
flutter-plugin:
static const EventChannel messageChannel = EventChannel('eventChannelStream');
Stream<String> get messageStream async* {
messageChannel.receiveBroadcastStream().map((message) {
return message;
});
}
Kotlin code on android-side:
class HelperPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
private lateinit var channel: MethodChannel
private var messageChannel: EventChannel? = null
private var eventSink: EventChannel.EventSink? = null
override fun onListen(arguments: Any?, eventSink: EventChannel.EventSink?) {
this.eventSink = eventSink
}
override fun onCancel(arguments: Any?) {
eventSink = null
messageChannel = null
}
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "methodChannel")
channel.setMethodCallHandler(this)
messageChannel = EventChannel(flutterPluginBinding.binaryMessenger, "eventChannelStream")
messageChannel?.setStreamHandler(this)
}
...
// Doing other stuff here
And how I want to use this messages in a BLoC to send them after an Event to the UI:
Stream<String> retrieveMessage(Stream<String> messageStream) async* {
await for (String message in messageStream) {
//await Future.delayed(const Duration(milliseconds: 1500));
yield message;
}
}
Upvotes: 4
Views: 6386
Reputation: 189
Ok, got it to work. It may be not the best method, but at least it works :)
So Here is how i've done it, maybe it can help someone else :):
Kotlin Code for Responses from Android:
class HelperPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
private lateinit var channel: MethodChannel
private var messageChannel: EventChannel? = null
private var eventSink: EventChannel.EventSink? = null
override fun onListen(arguments: Any?, eventSink: EventChannel.EventSink?) {
this.eventSink = eventSink
}
override fun onCancel(arguments: Any?) {
eventSink = null
messageChannel = null
}
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "methodChannel")
channel.setMethodCallHandler(this)
messageChannel = EventChannel(flutterPluginBinding.binaryMessenger, "eventChannelStream")
messageChannel?.setStreamHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
"getPlatformVersion" -> {
result.success(tellVersionNumber())
}
"tellJoke" -> {
result.success(tellJoke())
}
"cantAnswer" -> {
result.success(cantAnswer())
}
"whoAreYou" -> {
result.success(whoAreYou())
}
"currentTime" -> {
result.success(currentTime())
}
"upTime" -> {
result.success(upTime())
}
"greetUser" -> {
result.success(greetUser())
}
"systemInformation" -> {
result.success(systemInformation())
}
else -> {
result.notImplemented()
}
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
// Here are Functions, which add Strings to the EventChannel via eventSink?.success("String to Add")
}
Flutter-Plugin:
class ChatbotHelper extends ChatbotInterface {
static const MethodChannel _channel = MethodChannel('methodChannel');
static const EventChannel messageChannel = EventChannel('eventChannelStream');
Stream<String> get messageStream async* {
await for (String message in messageChannel.receiveBroadcastStream().map((message) => message)){
yield message;
}
}
@override
Future<void> platformVersion() async {
await _channel.invokeMethod('getPlatformVersion');
}
@override
Future<void> tellJoke() async {
await _channel.invokeMethod('tellJoke');
}
// other invokeMethods and answerQuestion function, which calls these invokeMethods depending on a String message
...
}
And here is how I use the Stream in my BLoC:
Future<void> _initializeAnswerStream(
InitializeMessageStreamEvent event, Emitter<ChatState> emit) async {
try{
await for(String message in retrieveMessageStream(_helper.messageStream)) {
add(AddResponseMessageEvent(responseMessage: message));
}
}catch(e){
print(e);
}
}
This BLoC-Event is called upon creating the corresponding BlocProvider in my Widget Tree to initialize the Stream and wait for messages.
edit: fixed some values I renamed to post it here
Upvotes: 3