flutter-webrtc安装示例

1、pubspec.yaml 添加以下flutter_webrtc库

flutter pub get 安装库

2、使用示例

复制代码
class GetUserMediaSample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _GetUserMediaSampleState();
  }
}

class _GetUserMediaSampleState extends State<GetUserMediaSample> {
  // 定义本地和远程视频渲染器
  final RTCVideoRenderer _localRenderer = RTCVideoRenderer();
  final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
  bool _isFrontCamera = false;
  // 本地媒体流(摄像头+麦克风)
  MediaStream? _localStream;

  // PeerConnection(简化示例中未实际连接)
  RTCPeerConnection? _peerConnection;

  @override
  void initState() {
    super.initState();
    // 初始化渲染器并获取本地媒体流
    _initializeRenderers();
  }

  @override
  void dispose() {
    // 释放资源
    _localRenderer.dispose();
    _remoteRenderer.dispose();
    _localStream?.dispose();
    _peerConnection?.close();
    super.dispose();
  }

  // 初始化视频渲染器
  Future<void> _initializeRenderers() async {
    await _localRenderer.initialize();
    await _remoteRenderer.initialize();
    await _getUserMedia(false);
  }

  // 获取摄像头和麦克风权限并绑定到渲染器
  Future<void> _getUserMedia(bool useFrontCamera) async {
    try {
      final devices = await navigator.mediaDevices.enumerateDevices();
      final videoDevices =
          devices.where((d) => d.kind == 'videoinput').toList();
      String? targetDeviceId;
      for (var device in videoDevices) {
        if (useFrontCamera && device.label.contains("front") ||
            !useFrontCamera && device.label.contains("back")) {
          targetDeviceId = device.deviceId;
          break;
        }
      }

      targetDeviceId ??= videoDevices.first.deviceId;

      // 定义媒体约束(视频+音频)
      final constraints = {
        'audio': true,
        'video': {
          'deviceId': targetDeviceId,
          'width': 1920,
          'height': 1080,
          'mandatory': {
            'minWidth': '640',
            'minHeight': '480',
            'minFrameRate': '30',
          },
        }
      };

      // 获取媒体流
      _localStream = await navigator.mediaDevices.getUserMedia(constraints);

      // 将本地流绑定到渲染器
      setState(() {
        _localRenderer.srcObject = _localStream;
      });
    } catch (e) {
      print("获取媒体设备失败: $e");
    }
  }

  void _switchCamera() async {
    setState(() {
      _isFrontCamera = !_isFrontCamera;
    });
    await _getUserMedia(_isFrontCamera);
  }

  // 构建 UI
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text('WebRTC 示例'),
      ),
      body: Column(
        children: [
          // 本地视频预览
          Expanded(
            child: Container(
              margin: EdgeInsets.all(8),
              decoration: BoxDecoration(
                border: Border.all(color: Colors.blue),
              ),
              child: RTCVideoView(_localRenderer),
            ),
          ),

          // 远程视频预览(简化示例中未实际连接)
          // Expanded(
          //   child: Container(
          //     margin: EdgeInsets.all(8),
          //     decoration: BoxDecoration(
          //       border: Border.all(color: Colors.red),
          //     ),
          //     child: RTCVideoView(_remoteRenderer),
          //   ),
          // ),

          // 控制按钮
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                icon: Icon(Icons.videocam_off),
                onPressed: _toggleVideo,
              ),
              IconButton(
                icon: Icon(Icons.mic_off),
                onPressed: _toggleAudio,
              ),
              IconButton(onPressed: _switchCamera, icon: Icon(Icons.cached))
            ],
          ),
        ],
      ),
    );
  }

  // 切换摄像头开关
  void _toggleVideo() {
    if (_localStream != null) {
      final videoTrack = _localStream!.getVideoTracks().first;
      setState(() {
        videoTrack.enabled = !videoTrack.enabled;
      });
    }
  }

  // 切换麦克风开关
  void _toggleAudio() {
    if (_localStream != null) {
      final audioTrack = _localStream!.getAudioTracks().first;
      setState(() {
        audioTrack.enabled = !audioTrack.enabled;
      });
    }
  }
}
相关推荐
程序员老刘1 天前
跨平台开发地图 | 2026年6月
flutter·ai编程·客户端
悟空瞎说2 天前
Flutter 架构详解:新手必懂底层原理
flutter
SoaringHeart2 天前
Flutter最佳实践:IM聊天文字链接自动识别跳转
前端·flutter
恋猫de小郭2 天前
KMP / CMP 鸿蒙版本 Beta 发布,他有什么特别之处?
android·前端·flutter
风华圆舞3 天前
Flutter + 鸿蒙 Intents Kit:页面直达能力的完整接入方案
flutter·ui·华为·harmonyos
韩曙亮3 天前
【Flutter】Flutter 组件 ④ ( 组件渲染 的 三棵树理论 | Widget 树 → Element 树 → RenderObject 树 )
flutter·element·widget·renderobject
恋猫de小郭3 天前
Android 17 正式版发布,全新 AI 和各种破坏性更新
android·前端·flutter
kingbal3 天前
Windows:flutter环境搭建
windows·flutter
911hzh3 天前
Flutter MethodChannel 跨端通信框架 zh_native_channel:快速入门、优势分析与 Pigeon 对比
flutter
911hzh3 天前
Flutter 快速搭建新项目:用 Flutter Foundation Kit 一条命令生成带基础架构的 App 模板
flutter