kalinor
kalinor

Reputation: 189

Receive Strings via EventChannel from Android

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

Answers (1)

kalinor
kalinor

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

Related Questions