'EventChannel not being called every time

I am very new to flutter+dart framework. I am trying to understand how EventChannel works. I have set up EventChannel to capture the number of an incoming call.

On the android side, I have set up an BroadcastReceiver as follows.

public class CallEventHandler extends BroadcastReceiver implements EventChannel.StreamHandler {
private static final String TAG = "[SAMPLE]";
private static final int NUMBER_LEN = 10;

private EventChannel.EventSink eventSink = null;
private Activity activity = null;

public CallEventHandler(Activity activity) {
    this.activity = activity;
}
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
    Log.i(TAG, "[onListen] setting up events");
    eventSink = events;
}

@Override
public void onCancel(Object arguments) {
    Log.i(TAG, "[onCancel] cancel events");
    eventSink = null;
    activity = null;
}

@Override
public void onReceive(Context context, Intent intent) {
    try {
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
        if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
            if(incomingNumber != null) {
                Log.i(TAG, "[CallEventHandler] Incoming number : " + incomingNumber);
                if(incomingNumber.length() > NUMBER_LEN) {
                    incomingNumber = incomingNumber.substring(incomingNumber.length() - NUMBER_LEN, incomingNumber.length());
                    Log.i(TAG, "[CallEventHandler] Incoming number after : " + incomingNumber);
                    if(activity != null) {
                        String finalIncomingNumber = incomingNumber;
                        activity.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                if(eventSink != null) {
                                    Log.i(TAG, "[CallEventHandler] HERESSSSS : " + finalIncomingNumber);
                                    eventSink.success(finalIncomingNumber);
                                }
                            }
                        });
                    }
                }
            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

}

In the onReceive method, I am getting the incoming number and I am sending it to EventSink.

In my MainActivity I am setting up the CallEventHandler as follows:

private final String eventId = "SAMPLE_ID";
private CallEventHandler handler = new CallEventHandler(this);

@Override
public void onStart() {
    super.onStart();
    ...
    registerReceiver(handler, filter);
}

@Override
public void onStop() {
    super.onStop();
    unregisterReceiver(handler);
}

@Override
public void configureFlutterEngine(@NonNull  FlutterEngine flutterEngine) {
    super.configureFlutterEngine(flutterEngine);
    new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), eventId)
            .setStreamHandler(handler);
}

On the Flutter side, the code is as follows:

class EventHandler {
    static const String TAG = "[SAMPLE]";
    final String _eventId = "SAMPLE_ID";
    EventChannel? _evtChannel;
    Stream<String>? _evtStream;

    EventHandler() {
        debugPrint(TAG + " Setting up EventHandler");
        _evtChannel = EventChannel(_eventId);
        _evtStream = _evtChannel?.receiveBroadcastStream().distinct().map((dynamic
        event) => getString(event as String));
    }

    void startListening(void Function(String data)? onData) {
        debugPrint(TAG + " starting listening");
        _evtStream?.listen((data) {
            debugPrint(TAG + " In listening");
            onData!(data);
        });
    }
}

In my UI code, I have a StatefulWidget (MySamplePage) where I am registering my callback when the call is received

void initState() {
    widget.handler.startListening((incomingNumber) {
      debugPrint(_tag + " data : $incomingNumber");
      ...
    }); 
}

In my stateful home page build method, I initialize the handler in initState and added a route in build method

class _HomeScreenState extends State<HomeScreen> {
    @override
    void initState() {
        super.initState();
        debugPrint(_tag + "initState");
        _handler = EventHandler();
    }

    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            routes: {
                '/caller': (context) => MySamplePage(
                    handler: _handler
                 ),
            },
            ...
        );
    }
}

The issue I am facing is that, when the widget is opened I am receiving the first incoming call, as expected. But if I make another call, then that second call is not captured by the stream. If I press the back button, and reopen the Widget everything works as expected, the first incoming call is printed in the console. I know the the Android code is sending the event from the onReceive method (The `HERESSSSS' line is printed every time), but the flutter stream is not getting the values. I am not sure what I am doing wrong here. Can anyone please help?

My log is

I/flutter (11836): [SAMPLE][HomeScreen]initState
I/flutter (11836): [SAMPLE][EventHandler] Setting up EventHandler
V/AutofillManager(11836): requestHideFillUi(null): anchor = null
I/flutter (11836): [SAMPLE][EventHandler] starting listening
I/[SAMPLE] (11836): [onListen] setting up events
I/[SAMPLE] (11836): [CallEventHandler] Receiver start
I/[SAMPLE] (11836): [CallEventHandler] Receiver start
I/[SAMPLE] (11836): [CallEventHandler] Incoming number : +91XXXXXXXXXX
I/[SAMPLE] (11836): [CallEventHandler] Incoming number after : XXXXXXXXXX
I/[SAMPLE] (11836): [CallEventHandler] HERESSSSS : XXXXXXXXXX
I/flutter (11836): [SAMPLE][EventHandler] In listening
I/flutter (11836): [SAMPLE] data : XXXXXXXXXX

In the subsequent incoming calls, the last line is not printed

Thank you



Solution 1:[1]

Ok, I have managed to resolve it, but don't know if this is the correct approach. The issue is that MySamplePage is a StatefulWidget, And I am calling setState in its State object. That might be the reason it's unable to listen to the stream anymore. I have called startListening is the setState method and changed the code accordingly (remove the previous subscription and re-listen to the stream)

void startListening(void Function(String data)? onData) {
    debugPrint(TAG + " starting listening");
    if(_subscription != null) {
        _subscription?.cancel();
        _subscription = null;
    }
    _subscription ??= _evtStream?.listen((data) {
        debugPrint(TAG + " In listening");
        onData!(data);
    });
}

Here _subscription is a variable of type StreamSubscription<String>?. Hope this answer is helpful. And I should have posted complete code earlier.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sudipta Roy