You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter集成Agora SDK直播:远端视图黑屏问题求助

Flutter集成Agora SDK直播:本地视图正常,远端黑屏求助

我正在尝试使用Agora SDK实现Flutter直播功能,本地视图显示正常,但远端视图出现黑屏问题。已尝试以下排查操作:

  • 重新检查所有权限是否启用
  • 在agora.io重新创建应用
  • 更换设备测试
  • 检查网络连接情况

以下是我的实现代码:

void initEngine() async {
    _engine = await RtcEngine.create(agoraKey);

    addListeners();
    _engine.enableVideo();
    _engine.startPreview();
    _engine.enableAudio();

    _engine.setChannelProfile(ChannelProfile.LiveBroadcasting);
    print(widget.isBroadcaster);
    if (("${widget.userData.uid}${widget.userData.name}" == widget.channeId)) {
      _engine.setClientRole(ClientRole.Broadcaster);
      print('broadcaster');
    } else {
      _engine.setClientRole(ClientRole.Audience);
      print('audience');
    }

    _joinChannel();
  }

  _renderVideo(
    user,
    isScreenSharing,
  ) {
    return AspectRatio(
      aspectRatio: 16 / 9,
      child: "${user.uid}${user.name}" == widget.channeId
          ? isScreenSharing
              ? kIsWeb
                  ? const RtcLocalView.SurfaceView.screenShare()
                  : const RtcLocalView.TextureView.screenShare()
              : const RtcLocalView.SurfaceView(
                  zOrderMediaOverlay: true,
                  zOrderOnTop: true,
                )
          : isScreenSharing
              ? kIsWeb
                  ? const RtcLocalView.SurfaceView.screenShare()
                  : const RtcLocalView.TextureView.screenShare()
              : isRendered
                  ? RtcRemoteView.TextureView(
                      uid: _remoteUids[0],
                      channelId: widget.channeId,
                    )
                  : Center(
                      child: Text(
                        'No Video',
                        style: GoogleFonts.balooPaaji2(
                          fontSize: 15,
                        ),
                      ),
                    ),
    );
  }

  void addListeners() {
    _engine.setEventHandler(RtcEngineEventHandler(
      joinChannelSuccess: (channel, uid, elapsed) async {
        debugPrint('joinChannelSuccess $channel $uid $elapsed');
        print(
            'joinChannelSuccess ======================================================> $uid');
      },
      leaveChannel: (stats) {
        debugPrint('leaveChannel $stats');
        setState(() {
          _remoteUids.clear();
        });
      },
      userJoined: (uid, elapsed) {
        debugPrint('userJoined $uid $elapsed');
        setState(() {
          print('userJoined=====================>:$_remoteUids');
          _remoteUids.add(uid);
          setState(() {
            isRendered = true;
          });
          print('=======>====>$_remoteUids');
        });
      },
      userOffline: (uid, reason) {
        debugPrint('userOffline=====================> $uid $reason');
        setState(() {
          _remoteUids.remove(uid);
        });
      },
      userMuteAudio: (uid, muted) {
        debugPrint('userMuteAudio $uid $muted');
      },
      userMuteVideo: (uid, muted) {
        debugPrint('userMuteVideo $uid $muted');
      },
      userEnableVideo: (uid, enabled) {
        debugPrint('userEnableVideo $uid $enabled');
      },
      error: (error) {
        debugPrint('error $error');
      },
    ));
    print('=====.$_remoteUids========');
  }

  void _joinChannel() async {
    //leave the current channel

    if (defaultTargetPlatform == TargetPlatform.iOS) {
      await [Permission.microphone, Permission.camera].request();
      await _engine.joinChannel(
          tempToken, 'testing_channel', widget.channeId, 0);
      debugPrint('joinChannelWithUserAccount ${widget.channeId} ');
    } else {
      await [Permission.microphone, Permission.camera].request();
      await _engine.joinChannel(
          tempToken, 'testing_channel', widget.channeId, 0);
      debugPrint('joinChannelWithUserAccount ${widget.channeId} ');
    }
  }
}

问题排查与修复方案

1. 频道ID不匹配(核心问题)

你在_joinChannel中调用joinChannel时传入的频道名是'testing_channel',但渲染远端视图时指定的channelId是widget.channeId(注意拼写错误:channeId少了字母l,应为channelId)。频道名不匹配会导致远端视图无法获取正确的流,直接造成黑屏。

修复代码:
统一频道ID并修正变量名拼写:

void _joinChannel() async {
  await [Permission.microphone, Permission.camera].request();
  // 使用正确的频道ID,同时修正变量名拼写错误
  await _engine.joinChannel(tempToken, widget.channelId, widget.channelId, 0);
  debugPrint('joinChannelWithUserAccount ${widget.channelId} ');
}

同时将所有widget.channeId替换为widget.channelId(包括initEngine、_renderVideo中的判断逻辑)。

2. 远端UID获取不安全

直接取_remoteUids[0]会在数组为空时触发越界错误,导致视图异常。添加空判断确保安全:

// 原远端视图渲染代码
? RtcRemoteView.TextureView(
    uid: _remoteUids[0],
    channelId: widget.channelId,
  )
// 修复后
? _remoteUids.isNotEmpty
    ? RtcRemoteView.TextureView(
        uid: _remoteUids[0],
        channelId: widget.channelId,
      )
    : Center(child: Text('等待远端用户加入'))

3. 嵌套setState冗余

userJoined回调中的嵌套setState会导致不必要的重绘,合并为一个:

setState(() {
  print('userJoined=====================>:$_remoteUids');
  _remoteUids.add(uid);
  isRendered = true;
  print('=======>====>$_remoteUids');
});

4. 权限请求校验

添加权限授权结果校验,避免极端场景异常:

void _joinChannel() async {
  final statuses = await [Permission.microphone, Permission.camera].request();
  if (statuses[Permission.microphone] != PermissionStatus.granted || 
      statuses[Permission.camera] != PermissionStatus.granted) {
    debugPrint('麦克风/相机权限未授权');
    return;
  }
  await _engine.joinChannel(tempToken, widget.channelId, widget.channelId, 0);
  debugPrint('joinChannelWithUserAccount ${widget.channelId} ');
}

额外检查点

  • 确认tempToken有效性:临时Token有过期时间,过期会导致无法拉取远端流;
  • 验证主播角色设置:检查initEngine中的主播判断逻辑是否正确,确保主播端确实以Broadcaster角色推流;
  • 查看Agora控制台日志:检查频道内用户连接状态和流发布情况,确认远端流是否正常推送。

内容的提问来源于stack exchange,提问作者Dark Code Cat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 14:24:15