Skip to main content

nativemind_call 0.1.1 copy "nativemind_call: ^0.1.1" to clipboard
nativemind_call: ^0.1.1 copied to clipboard

Native media layer of NativeMind calls on Andrey Bekhtold's handmade WebRTC code (Android PeerConnectionClient, iOS WebRTCManager, the WebRTCClientType configuration, Debug.swift): video through Flutt [...]

example/lib/main.dart

// Harness of nativemind_call: Andrey Bekhtold's loopback call (CallFragment.setupWebRTC: peer A sends the camera,
// peer B receives) in one process, shown through NmNativeCallPort.videoTile — the local tile is A's texture, the
// remote tile is the video B received over WebRTC. `--dart-define=NM_AUTOSTART=true` starts the call at launch.
// written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
import 'package:flutter/material.dart';
import 'package:nativemind_call/nativemind_call.dart';
import 'package:nativemind_deals/nativemind_deals.dart';

// written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
void main() => runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: LoopbackPage()));

/// The loopback screen.
// written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
class LoopbackPage extends StatefulWidget {
  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  const LoopbackPage({super.key, this.call});

  /// Engine (tests inject one over a fake host).
  final NmNativeCall? call;

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  @override
  State<LoopbackPage> createState() => _LoopbackPageState();
}

// written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
class _LoopbackPageState extends State<LoopbackPage> {
  NmLoopbackCall? _loopback;
  NmNativeCallPort? _port;
  bool _audio = false;
  String _status = 'idle';

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  NmNativeCall get _call => widget.call ?? NmNativeCall.instance;

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  @override
  void initState() {
    super.initState();
    if (const bool.fromEnvironment('NM_AUTOSTART')) WidgetsBinding.instance.addPostFrameCallback((_) => _start());
  }

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  Future<void> _start() async {
    if (_loopback != null) return;
    setState(() => _status = 'permissions…');
    final granted = await _call.requestPermissions();
    if (!granted) {
      if (mounted) setState(() => _status = 'camera / microphone permission denied');
      return;
    }
    final loopback = NmLoopbackCall(call: _call);
    await loopback.setupWebRTC();
    // Loudspeaker loop: A's microphone would play on B — start muted.
    await loopback.peerA.setAudioEnabled(_audio);
    loopback.peerB.iceConnectionState.addListener(() {
      if (mounted) setState(() => _status = 'B: ${loopback.peerB.iceConnectionState.value}');
    });
    if (!mounted) return;
    setState(() {
      _loopback = loopback;
      _port = NmNativeCallPort(
        client: loopback.peerA,
        remoteClient: loopback.peerB,
        activeCall: const NmCallInfo(clientName: 'A', expertName: 'B'),
        onAccept: () async {},
        onHangUp: _stop,
        call: _call,
      );
      _status = 'offer sent';
    });
  }

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  Future<void> _stop() async {
    final loopback = _loopback;
    if (loopback == null) return;
    setState(() {
      _loopback = null;
      _port = null;
      _status = 'idle';
    });
    await loopback.dispose();
  }

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  Future<void> _toggleAudio() async {
    setState(() => _audio = !_audio);
    await _loopback?.peerA.setAudioEnabled(_audio);
  }

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  @override
  void dispose() {
    _loopback?.dispose();
    super.dispose();
  }

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  Widget _tile(String label, NmCallTile tile) {
    final port = _port;
    return Expanded(
      child: Container(
        margin: const EdgeInsets.all(8),
        decoration: BoxDecoration(color: Colors.black12, borderRadius: BorderRadius.circular(16)),
        clipBehavior: Clip.antiAlias,
        child: Stack(
          fit: StackFit.expand,
          children: [
            if (port != null) port.videoTile(context, tile) else const NmNoVideoWidget(),
            Positioned(
              left: 12,
              top: 8,
              child: Text(
                label,
                style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600, shadows: [Shadow(blurRadius: 4)]),
              ),
            ),
          ],
        ),
      ),
    );
  }

  // written by Claude Code (generated by Claude Code), 2026-10-13; SDD: flows/sdd-widgets-handmade
  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text('nativemind_call · loopback')),
    body: SafeArea(
      child: Column(
        children: [
          _tile('A — camera (local tile)', NmCallTile.local),
          _tile('B — received over WebRTC (remote tile)', NmCallTile.remote),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Text(_status, key: const Key('status')),
          ),
          Padding(
            padding: const EdgeInsets.all(12),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                FilledButton(key: const Key('start'), onPressed: _loopback == null ? _start : null, child: const Text('Start')),
                OutlinedButton(onPressed: _loopback == null ? null : _toggleAudio, child: Text(_audio ? 'Mute mic' : 'Unmute mic')),
                OutlinedButton(key: const Key('stop'), onPressed: _loopback == null ? null : () => _port?.hangUp(), child: const Text('Stop')),
              ],
            ),
          ),
        ],
      ),
    ),
  );
}
0
likes
130
points
99
downloads

Documentation

API reference

Publisher

verified publisherframework.nativemind.net

Weekly Downloads

Native media layer of NativeMind calls on Andrey Bekhtold's handmade WebRTC code (Android PeerConnectionClient, iOS WebRTCManager, the WebRTCClientType configuration, Debug.swift): video through Flutter textures, hardware codecs, a Pigeon API after WebRTCManagerDelegate, and NmNativeCallPort — the NmCallPort of nativemind_deals.

Homepage
Repository (GitLab)

License

unknown (license)

Dependencies

flutter, meta, nativemind_deals

More

Packages that depend on nativemind_call

Packages that implement nativemind_call