BraveEvidence
BraveEvidence

Reputation: 57

Flutter's EventChannel not sending data to dart side

I am trying to show my app in share sheet of android and I was able to successfully do it but I am not able to send data from native side to dart using EventChannel.

I have updated my MainActivity to following

class MainActivity: FlutterActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Log.i("TAG123","onCreate")
        onSharedIntent()
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        Log.i("TAG123","configure flutter engine")


    }

    private fun onSharedIntent() {
        val receivedAction: String? = intent.action
        val receivedType: String? = intent.type
        if (receivedAction == Intent.ACTION_SEND) {

            // check mime type
            if (receivedType?.startsWith("text/") == true) {
                val receivedText: String? = intent.getStringExtra(Intent.EXTRA_TEXT)
                if (receivedText != null) {
                    //do your stuff
                    Log.e("TAG123", receivedText.toString())
                    if(flutterEngine?.dartExecutor?.binaryMessenger != null){
                        Log.i("TAG123","flutter engine not null")
                    }
                    Log.e("TAG123", "here")
                    EventChannel(
                        flutterEngine!!.dartExecutor.binaryMessenger,
                        "sharePlatform"
                    ).setStreamHandler(object : EventChannel.StreamHandler {

                        override fun onListen(arguments: Any, events: EventSink) {
                            Log.e("TAG123", "here123")
                            events.success(receivedText)
                        }

                        override fun onCancel(arguments: Any) {
                            Log.e("TAG123", "here123456")
                        }
                    })
                } else {
                    Log.e("TAG123", "no data")
                }
            } else if (receivedType?.startsWith("image/") == true) {
//                val receiveUri: Uri? = intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri?

                val receiveUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
                } else {
                    intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri?
                }

                if (receiveUri != null) {
                    Log.e("TAG123", receiveUri.toString())
                    if(flutterEngine?.dartExecutor?.binaryMessenger != null){
                        Log.i("TAG123","flutter engine not null")
                    }
                    EventChannel(
                        flutterEngine!!.dartExecutor.binaryMessenger,
                        "sharePlatform"
                    ).setStreamHandler(object : EventChannel.StreamHandler {

                        override fun onListen(arguments: Any, events: EventSink) {
                            Log.e("TAG123", "here123")
                            events.success(receiveUri)
                        }

                        override fun onCancel(arguments: Any) {
                            Log.e("TAG123", "here123456")
                        }
                    })
                } else {
                    Log.e("TAG123", "no image data")
                }
            }
        } else if (receivedAction == Intent.ACTION_MAIN) {
            Log.e("TAG123", "onSharedIntent: nothing shared")
        }
    }

}

I have made the required changes to AndroidManifest as well

<intent-filter>
                <action android:name="android.intent.action.SEND" />

                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="image/*" />
                <data android:mimeType="text/*" />
            </intent-filter>

I can see the url or uri of image when I share it from some other app to my app in logs but EventChannel never works

Below is my dart code

class _MyHomePageState extends State<MyHomePage> {
  var data = "";
  final _eventChannel = const EventChannel('sharePlatform');

  @override
  void initState() {
    super.initState();

    _eventChannel.receiveBroadcastStream().distinct().map((dynamic event) {
      debugPrint("TAG123Flutter $event");
      setState(() {
        data = event;
      });
      // return event;
    });
  }......

Upvotes: 1

Views: 1043

Answers (4)

Mearaj
Mearaj

Reputation: 1911

I went through this blog to resolve your issue and came upon with the following solution.

Replace your MainActivity Code with the following

class MainActivity: FlutterActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    Log.i("TAG123","onCreate")
}

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    Log.i("TAG123","configure flutter engine")
    EventChannel(
        flutterEngine.dartExecutor.binaryMessenger,
        "sharePlatform"
    ).setStreamHandler(object : EventChannel.StreamHandler {

        override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
            Log.e("TAG123", "configure here123")
            Log.e("TAG123", "$intent")
            onSharedIntent(events)
        }

        override fun onCancel(arguments: Any?) {
            Log.e("TAG123", "configure here123456")
        }
    })
}

private fun onSharedIntent(events: EventChannel.EventSink) {
    val receivedAction: String? = intent.action
    val receivedType: String? = intent.type
    if (receivedAction == Intent.ACTION_SEND) {

        // check mime type
        if (receivedType?.startsWith("text/") == true) {
            val receivedText: String? = intent.getStringExtra(Intent.EXTRA_TEXT)
            if (receivedText != null) {
                //do your stuff
                Log.e("TAG123", receivedText.toString())
                if(flutterEngine?.dartExecutor?.binaryMessenger != null){
                    Log.i("TAG123","flutter engine not null")
                }
                Log.e("TAG123", "here")
                events.success(receivedText)
            } else {
                Log.e("TAG123", "no data")
            }
        } else if (receivedType?.startsWith("image/") == true) {
//                val receiveUri: Uri? = intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri?

            val receiveUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
            } else {
                intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri?
            }
            if (receiveUri != null) {
                Log.e("TAG123", receiveUri.toString())
                if(flutterEngine?.dartExecutor?.binaryMessenger != null){
                    Log.i("TAG123","flutter engine not null")
                }
                events.success(receiveUri.toString())
            } else {
                Log.e("TAG123", "no image data")
            }
        }
    } else if (receivedAction == Intent.ACTION_MAIN) {
        Log.e("TAG123", "onSharedIntent: nothing shared")
    }
  }

}

Replace your dart code with the following

  @override
  void initState() {
  super.initState();
  _eventChannel.receiveBroadcastStream().listen((dynamic event) {
    debugPrint("TAG123Flutter $event");
    setState(() {
      data = event;
    });
    // return event;
  });
}

Hope this helps. Thanks :)

Upvotes: 1

AslamThachapalli
AslamThachapalli

Reputation: 54

You are calling onSharedIntent method inside onCreate method directly. Looks like that can be the issue because onSharedIntent won't be invoked in this case. You should wrap the onSharedIntent inside a Handler like this:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Log.i("TAG123","onCreate")

        /// Wrap onSharedIntent with this Handler.
        Handler(Looper.getMainLooper()).postDelayed(Runnable {
          onSharedIntent()  
        }, 0)
    }

Try this. This should work.


Update continued from the comment.

There doesn't seem any obvious reason. But you can also try to make the streamHandler another class rather than an object type. Just try it as a hack as you said the problem seems to be in the eventChannel invocation.

Do this:

class MyStreamHandler(private val text: String) : EventChannel.StreamHandler{
    override fun onListen(arguments: Any, events: EventSink) {
        Log.e("TAG123", "here123")
        events.success(text)
    }

    override fun onCancel(arguments: Any) {
        Log.e("TAG123", "here123456")
    }
}

Then call:

EventChannel(flutterEngine!!.dartExecutor.binaryMessenger,
             "sharePlatform").setStreamHandler(
                              MyStreamHandler(receivedText))

Upvotes: 0

justHooman
justHooman

Reputation: 3054

I think the reason is because of

EventChannel(flutterEngine!!.dartExecutor.binaryMessenger, "sharePlatform").setStreamHandle

Why do you need to use flutterEngine!!.dartExecutor.binaryMessenger?

in Flutter you create EventChannel like this EventChannel('sharePlatform')

So I think in ADR code you should use the default executor as messager like this: EventChannel(flutterEngine!!.dartExecutor, "sharePlatform")

Hope it helps.

Upvotes: 0

inyourdream
inyourdream

Reputation: 730

This is because you didn't subscribe to the stream. You are operating on the stream only at the moment MyHomePage is initialized. If you're not familiar with the topic, I suggest studying event driven programming more, it's confusing in Dart to read write visualize it.

StreamSubscription<dynamic>? _eventSubscription;

  @override
  void initState() {
    super.initState();

    _eventSubscription = _eventChannel.receiveBroadcastStream().listen((dynamic event) {
      debugPrint("TAG123Flutter $event");
      setState(() {
        data = event;
      });
    });

  }

  @override
  void dispose() {
    _eventSubscription?.cancel(); // Cancel the event subscription when the widget is disposed
    super.dispose();
  }

This way the widget will listen to events that come as long as it is alive.

If you want to use distinct, you can use a StreamTransformer. For example,

StreamTransformer<String, String> distinctTransformer =
    StreamTransformer<String, String>.fromHandlers(
  handleData: (String data, EventSink<String> sink) {
    Set<String> uniqueItems = Set<String>();
    if (!uniqueItems.contains(data)) {
      uniqueItems.add(data);
      sink.add(data);
    }
  },
);

StreamSubscription<String> _streamSubscription;
_streamSubscription = _streamController.stream
    .transform(distinctTransformer)
    .listen((data) {
      print('Received distinct data: $data');
    });

Upvotes: 0

Related Questions