peer_rtc 1.0.0 copy "peer_rtc: ^1.0.0" to clipboard
peer_rtc: ^1.0.0 copied to clipboard

Simple peer-to-peer with WebRTC for Dart. PeerJS port for Flutter.

example/lib/main.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:peer_rtc/peer_rtc.dart';

import 'widgets/widgets.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final jsonString = await rootBundle.loadString('assets/peer_option.json');
  final json = jsonDecode(jsonString) as Map<String, dynamic>;
  runApp(App(json: json));
}

class App extends StatelessWidget {
  const App({super.key, required this.json});
  final Map<String, dynamic> json;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'PeerRTC',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: CyberTheme.bgDark,
      ),
      home: PeerRTCPage(json: json),
    );
  }
}

class PeerRTCPage extends StatefulWidget {
  const PeerRTCPage({super.key, required this.json});
  final Map<String, dynamic> json;

  @override
  State<PeerRTCPage> createState() => _PeerRTCPageState();
}

class _PeerRTCPageState extends State<PeerRTCPage> {
  // ══════════════════════════════════════════════════════════════
  // STATE
  // ══════════════════════════════════════════════════════════════
  Peer? _peer;
  String _status = 'INIT';
  String _peerId = '';
  bool _isChannelOpen = false;
  DataConnection? _dataConnection;

  final List<String> _logs = [];
  final _remoteIdController = TextEditingController();
  final _messageController = TextEditingController();
  final _scrollController = ScrollController();

  // ══════════════════════════════════════════════════════════════
  // LIFECYCLE
  // ══════════════════════════════════════════════════════════════
  @override
  void initState() {
    super.initState();
    _connect();
  }

  @override
  void dispose() {
    _peer?.dispose();
    _remoteIdController.dispose();
    _messageController.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  // ══════════════════════════════════════════════════════════════
  // PEER CONNECTION
  // ══════════════════════════════════════════════════════════════
  void _connect() {
    _log('Connecting to signaling server...');
    setState(() => _status = 'CONNECTING');
    _peer = Peer(options: PeerOptions.fromJson(widget.json));

    _peer!.onOpen.listen((id) {
      setState(() {
        _peerId = id ?? '';
        _status = 'READY';
      });
      _log('Connected! ID: $id');
    });

    _peer!.onError.listen((e) => _log('ERROR: $e'));
    _peer!.onDisconnected.listen((_) {
      setState(() => _status = 'OFFLINE');
      _log('Disconnected');
    });
    _peer!.onReconnecting.listen((n) {
      setState(() => _status = 'RETRY');
      _log('Reconnecting #$n...');
    });
    _peer!.onReconnected.listen((_) {
      setState(() => _status = 'READY');
      _log('Reconnected!');
    });

    _peer!.onConnection.listen((conn) {
      _log('Incoming: ${conn.peer}');
      _setupDataConnection(conn);
    });
  }

  // ══════════════════════════════════════════════════════════════
  // DATA CONNECTION
  // ══════════════════════════════════════════════════════════════
  void _setupDataConnection(DataConnection conn) {
    _dataConnection = conn;
    setState(() => _isChannelOpen = false);

    conn.onOpen.listen((_) {
      _log('Channel OPEN with ${conn.peer}');
      setState(() => _isChannelOpen = true);
    });

    conn.onData.listen((data) => _log('< ${data['message'] ?? data}'));
    conn.onError.listen((e) => _log('Channel error: $e'));
    conn.onClose.listen((_) {
      _log('Channel CLOSED');
      setState(() {
        _dataConnection = null;
        _isChannelOpen = false;
      });
    });
  }

  void _connectToPeer() {
    final id = _remoteIdController.text.trim();
    if (id.isEmpty || _peer == null) return;
    _log('Connecting to $id...');
    _setupDataConnection(_peer!.connect(id));
    _remoteIdController.clear();
  }

  void _sendMessage() {
    final msg = _messageController.text.trim();
    if (msg.isEmpty || !_isChannelOpen) return;
    _dataConnection!.send({'message': msg});
    _log('> $msg');
    _messageController.clear();
  }

  // ══════════════════════════════════════════════════════════════
  // HELPERS
  // ══════════════════════════════════════════════════════════════
  void _log(String msg) {
    final ts = DateTime.now().toString().substring(11, 19);
    setState(() => _logs.add('[$ts] $msg'));
    Future.delayed(const Duration(milliseconds: 50), () {
      if (_scrollController.hasClients) {
        _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
      }
    });
  }

  void _copyId() {
    if (_peerId.isEmpty) return;
    Clipboard.setData(ClipboardData(text: _peerId));
    _log('Copied to clipboard!');
  }

  // ══════════════════════════════════════════════════════════════
  // UI - Layout: Header | Logs | Input
  // ══════════════════════════════════════════════════════════════
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            // Header: ME | PARTNER | STATUS
            HeaderMetrics(
              peerId: _peerId,
              partnerId: _dataConnection?.peer,
              status: _status,
              isChannelOpen: _isChannelOpen,
              onCopyId: _copyId,
            ),
            // Logs
            Expanded(
              child: LogsSection(
                logs: _logs,
                scrollController: _scrollController,
              ),
            ),
            // Input at bottom
            TerminalInput(
              isChannelOpen: _isChannelOpen,
              isConnected: _peer != null,
              remoteIdController: _remoteIdController,
              messageController: _messageController,
              onConnect: _connectToPeer,
              onSend: _sendMessage,
              onReconnect: _connect,
            ),
          ],
        ),
      ),
    );
  }
}
4
likes
0
points
182
downloads

Publisher

verified publisherfreetalk.io.vn

Weekly Downloads

Simple peer-to-peer with WebRTC for Dart. PeerJS port for Flutter.

Homepage

License

unknown (license)

Dependencies

events_emitter, flutter, flutter_webrtc, http, web_socket_channel

More

Packages that depend on peer_rtc