call_state_handler 2.0.0 copy "call_state_handler: ^2.0.0" to clipboard
call_state_handler: ^2.0.0 copied to clipboard

A Flutter plugin that detects phone calls and video calls on both Android and iOS platforms. This plugin helps you monitor call states in your app, allowing you to take appropriate actions when calls [...]

Call State Handler #

A Flutter plugin that detects active phone calls on Android and iOS, and VoIP/video calls (Google Meet, Zoom, WhatsApp, Teams, ...) on Android. Use it to pause or block parts of your app while the user is on a call.

Features #

  • Detects when a phone call starts and ends (Android and iOS)
  • Detects VoIP/video calls on Android
  • No CallKit on iOS, so apps using this plugin can be distributed in the China mainland App Store
  • No permissions required
  • Does not modify your app's audio session
  • Stream-based API that emits the current state to every new listener

Platform support #

Phone calls VoIP / video calls
Android ✅ ✅
iOS ✅ ❌ (see below)

How It Works #

Android #

The plugin checks the system audio mode (AudioManager.getMode()) once per second:

  • MODE_IN_CALL / MODE_RINGTONE (and call screening/redirect modes) → CallType.phoneCall
  • MODE_IN_COMMUNICATION → CallType.videoCall, reported only after the mode has been stable for about 3 seconds

Some non-call features also switch the device into MODE_IN_COMMUNICATION, for example voice-message recorders, voice chat in games and some Bluetooth headsets. If your app records audio itself, turn VoIP detection off while recording (see Recording audio in your app).

iOS #

Phone calls are detected with CoreTelephony's CTCallCenter.

iOS offers no reliable way to detect VoIP/video calls from other apps without CallKit. Apps that use CallKit are not allowed in the China mainland App Store, so this plugin does not use it and reports only regular phone calls on iOS. setVoipDetectionEnabled does nothing on iOS.

Usage #

Basic Implementation #

import 'package:call_state_handler/call_state_handler.dart';
import 'package:flutter/material.dart';

class CallMonitorExample extends StatefulWidget {
  const CallMonitorExample({super.key});

  @override
  State<CallMonitorExample> createState() => _CallMonitorExampleState();
}

class _CallMonitorExampleState extends State<CallMonitorExample> {
  final CallStateHandler _callStateHandler = CallStateHandler();

  @override
  void initState() {
    super.initState();
    _callStateHandler.initialize();
  }

  @override
  void dispose() {
    _callStateHandler.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Call Monitor Example')),
      body: StreamBuilder<CallState>(
        stream: _callStateHandler.onCallStateChanged,
        initialData: _callStateHandler.currentState,
        builder: (context, snapshot) {
          final callState = snapshot.data!;
          return Center(
            child: Text(
              callState.isCallActive
                  ? 'Call Active: ${callState.callType == CallType.phoneCall ? "Phone Call" : "Video Call"}'
                  : 'No Active Call',
              style: Theme.of(context).textTheme.titleLarge,
            ),
          );
        },
      ),
    );
  }
}

With BLoC/Cubit #

import 'dart:async';

import 'package:call_state_handler/call_state_handler.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CallMonitorCubit extends Cubit<CallState> {
  final CallStateHandler _callStateHandler = CallStateHandler();
  StreamSubscription<CallState>? _subscription;

  CallMonitorCubit() : super(const CallState.initial()) {
    _subscription = _callStateHandler.onCallStateChanged.listen(emit);
    _callStateHandler.initialize();
  }

  @override
  Future<void> close() async {
    await _subscription?.cancel();
    await _callStateHandler.dispose();
    return super.close();
  }
}

Recording audio in your app #

If your app records voice messages or otherwise uses the microphone, disable VoIP detection while recording. Otherwise the recording can be reported as a video call on Android:

await CallStateHandler().setVoipDetectionEnabled(false);
try {
  await recorder.start(...);
  // ...
  await recorder.stop();
} finally {
  // Always re-enable, even if recording fails; the flag is global.
  await CallStateHandler().setVoipDetectionEnabled(true);
}

Phone calls are still detected while VoIP detection is disabled.

API notes #

  • CallStateHandler() is a singleton, and initialize()/dispose() are reference counted. Several widgets can use it at the same time, as long as each one balances its initialize() with one dispose(). Monitoring stops only when the last one disposes.
  • onCallStateChanged emits the current state immediately to each new listener, then every change.
  • currentState returns the latest known state synchronously.
  • When the last dispose() stops monitoring, an inactive state is emitted, so a call-blocking UI is never left stuck. The stream stays usable, and you can call initialize() again later.

Migrating from 1.x #

  • iOS no longer uses CallKit and no longer reports CallType.videoCall.
  • Android no longer inspects the foreground app. You can remove the PACKAGE_USAGE_STATS and QUERY_ALL_PACKAGES permissions if you added them only for this plugin.
  • The plugin no longer declares READ_PHONE_STATE, and NSCallingCapabilityUsageDescription is no longer needed.
  • CallState now has value equality and a const constructor.

Contributing #

Contributions are welcome! If you find any issues or have suggestions for improvements:

Open an issue on GitHub Fork the repository Create a pull request with your changes

MIT License #

Copyright (c) 2025 Abdusamad

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

7
likes
140
points
1.49k
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin that detects phone calls and video calls on both Android and iOS platforms. This plugin helps you monitor call states in your app, allowing you to take appropriate actions when calls start or end.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on call_state_handler

Packages that implement call_state_handler