Introducing flutter_rust_sip: High-Performance SIP for Flutter via Rust FFI

Sep 13, 2026 min read

Integrating real-time communications like SIP (Session Initiation Protocol) and VoIP into Flutter applications has historically been challenging. Developers typically face a tradeoff:

  1. Pure Dart implementations: Often miss crucial industrial features like adaptive jitter buffers, hardware-accelerated codecs, acoustic echo cancellation (AEC), and reliable NAT traversal.
  2. Platform Channels to native libraries: Splitting logic across separate iOS, Android, and desktop native codebases leads to massive code duplication, inconsistent behavior across platforms, and high maintenance overhead.

To bridge this gap with zero compromise on performance and safety, I developed and published flutter_rust_sip on pub.dev.


Why PJSIP + Rust?

PJSIP is the industry gold standard C library for multimedia communications (SIP, SDP, RTP, RTCP). It powers countless production telecom platforms and softphones worldwide. However, exposing complex C APIs and asynchronous callbacks directly to Dart can quickly become error-prone and unsafe.

By introducing Rust as the intermediary layer:

  • Memory Safety & Concurrency: Rust manages low-level PJSIP structures, thread boundaries, and event queues safely without segfaults.
  • Modern FFI via flutter_rust_bridge: Data models, events, and asynchronous handlers cross the Flutter ↔ Rust barrier seamlessly with near-zero serialization overhead.
  • Cross-Platform Uniformity: The core SIP logic, account registration, and call state machines run identical native code across Android, Linux, and Windows.

Architecture & My Contributions

Here is how the components fit together:

┌─────────────────────────────────────────────────────────┐
│                     Flutter / Dart                      │
│      (Reactive Streams, UI State, flutter_rust_sip)     │
└────────────────────────────┬────────────────────────────┘
                             │  flutter_rust_bridge (FFI)
┌────────────────────────────▼────────────────────────────┐
│                        Rust Core                        │
│            (Thread management, Event loops)             │
└────────────────────────────┬────────────────────────────┘
                             │  pjsip-sys (Rust bindings)
┌────────────────────────────▼────────────────────────────┐
│                        PJSIP (C)                        │
│      (SIP stack, media transport, audio pipelines)      │
└─────────────────────────────────────────────────────────┘

1. The Low-Level Foundation: pjsip-sys

To make PJSIP accessible to Rust, I built and maintain pjsip-sys. It generates the Rust FFI bindings and handles linking against native dependencies (SSL, crypto, ALSA on Linux, Android NDK audio, etc.).

2. The Flutter Package: flutter_rust_sip

Published on pub.dev (verified publisher: shamortie.com), flutter_rust_sip provides idiomatic Dart APIs over the underlying Rust engine:

  • Reactive state: Uses RxDart BehaviorSubjects and Streams so the UI can effortlessly listen to registration states, incoming calls, and connection changes.
  • Account & Credential Management: Clean classes for SIP registrar servers, authentication, and transport protocols.
  • Call Session Control: Methods for making, answering, holding, and terminating calls.
  • Cross-Compilation Setup: Streamlined Android NDK cross-compilation toolchain and desktop build configurations.

Quick Look: Using flutter_rust_sip

Getting started in your Flutter project is straightforward:

dependencies:
  flutter_rust_sip: ^1.1.5

Initializing the client and registering a SIP account:

import 'package:flutter_rust_sip/flutter_rust_sip.dart';

Future<void> initSipClient() async {
  // 1. Initialize the native SIP backend
  await FlutterRustSip.init();

  // 2. Listen to registration state changes reactively
  FlutterRustSip.registrationStateStream.listen((state) {
    print('SIP Registration State: $state');
  });

  // 3. Register your SIP account
  await FlutterRustSip.registerAccount(
    username: "1001",
    password: "secretpassword",
    domain: "sip.myprovider.com",
    transport: SipTransport.udp,
  );
}

Handling incoming and outgoing calls:

// Listen to incoming call events
FlutterRustSip.callStateStream.listen((call) {
  if (call.state == CallState.incoming) {
    print('Incoming call from: ${call.remoteUri}');
  }
});

// Making an outbound call
await FlutterRustSip.makeCall("sip:1002@sip.myprovider.com");