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

P2P WebRTC library for Dart/Flutter with mesh networking and binary optimization for real-time games.

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 'game/star_pong.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),
      home: const StarPong(),
    );
  }
}

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;
  int _retryCount = 0;

  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;
        _retryCount = 0; // Reset retry count
      });
    });

    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;
      });

      // Auto-reconnect logic
      if (_peer != null && !_peer!.destroyed) {
        final remoteId = conn.peer;

        // Exponential Backoff: 2s, 4s, 8s, 16s... (cap at 30s)
        final delay = Duration(seconds: (2 * (1 << _retryCount)).clamp(2, 30));

        _log(
          'Lost connection. Retry #${_retryCount + 1} in ${delay.inSeconds}s...',
        );

        Future.delayed(delay, () {
          if (_peer?.destroyed == false && _dataConnection == null) {
            _retryCount++;
            _connectToPeerById(remoteId);
          }
        });
      }
    });
  }

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

  void _connectToPeerById(String id) {
    _log('Re-connecting to $id...');
    _setupDataConnection(_peer!.connect(id));
  }

  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
150
points
182
downloads
screenshot

Publisher

verified publisherfreetalk.io.vn

Weekly Downloads

P2P WebRTC library for Dart/Flutter with mesh networking and binary optimization for real-time games.

Homepage

Topics

#mesh #p2p #networking #peer #game

Documentation

API reference

Funding

Consider supporting this project:

paypal.me

License

unknown (license)

Dependencies

events_emitter, flutter, flutter_webrtc, http, web_socket_channel

More

Packages that depend on peer_rtc