Receive Strings via EventChannel from Android

1,078

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

Share:
1,078
kalinor
Author by

kalinor

Updated on December 01, 2022

Comments

  • kalinor
    kalinor over 1 year

    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;
      }
    }
    
    • pskink
      pskink over 2 years
      "I know that I still have to rely on the MethodChannel to trigger methods on android side" - thats not true, check raywenderlich.com/… for example
    • kalinor
      kalinor over 2 years
      I already tried working with this tutorial, but I still could not get it to work. sending a request to android to start a specific method / function is already working, and currently I'm also getting a response from Android via the Methodchannel. But when it comes to return my own Stream with Eventchannel, I have problems. Especially since I don't want to send something like some device data, instead some predefined strings. For example I send the String "Hello" to the device and I want to recieve a specific string (or multiple Strings) as answer from the device via stream
    • pskink
      pskink over 2 years
      and where are you calling eventSink?.success(<some_data>)?
    • kalinor
      kalinor over 2 years
      Currently in one of my functions on android side, which are triggered and send back the String lists via MethodChannel. Currently I just added it to one of these functions. Just forgot to add the code part for that, since the MethodChannel Functions are working just fine and therefore should be irrelevant for my problem :)