본문 바로가기
Flutter for Beginners

Flutter 영상통화

by Records that rule memory 2025. 4. 15.
728x90

영상통화에 어플리케이션 개발시 고려할 기술적 요소들

Flutter로 영상통화 어플리케이션을 만들기 위해서는 다음과 같은 기술적인 준비와 고려사항이 필요합니다.

크게는 영상 처리, 네트워크 통신, 권한 관리, UI/UX 설계, 백엔드 서버 구성, 보안 및 성능 최적화로 나눌 수있습니다.

1. 영상통화 기능 구현 (RTC 기술)

Flutter 자체는 영상 처리 기능이 없기 때문에, WebRTC 기반의 외부 SDK를 사용해야 합니다.

주요 영상통화 SDK (Flutter에서 사용 가능한 것):

SDK 설명
Agora Flutter 플러그인 제공. 안정성 높고 문서 풍부. 무료 요금제 있음.
ZEGOCLOUD 실시간 통화, 채팅, 라이브 지원. UI 위젯까지 제공.
100ms 빠른 개발을 위한 Prebuilt UI 제공. 음성/영상통화에 특화.
Twilio 강력한 백엔드 통합과 높은 확장성. 다소 복잡함.
Daily.co WebRTC 기반. 비교적 간단하게 사용 가능.

추천: 초기에 빠르게 개발하고 싶다면 Agora Flutter SDK 또는 ZEGOCLOUD 사용 권장.

2. 필수 플러그인

3. 권한 처리

모바일 플랫폼에서 마이크, 카메라 권한을 요청해야 합니다. 자세한 내용은 밑에서 자세히 설명하겠습니다.

4. 백엔드 / 서버 구성

영상통화 자체는 P2P 또는 중계서버를 이용할 수 있지만, 다음 요소를 서버에서 처리해야 합니다.

  • 유저 인증 및 세션 관리
  • 채널 생성/입장 관리
  • 푸시 알림 (통화 수신 알림 등)
  • 통화 로그 저장 (선택)

Firebase, Supabase, 혹은 직접 구축한 Spring/Node.js 백엔드와 연동 가능

5. UI/UX 설계

  • 카메라 프리뷰 (Local/Remote View)
  • 통화 시작/종료 버튼
  • 마이크 ON/OFF, 카메라 ON/OFF 버튼
  • 통화 연결 상태, 사용자 상태 표시
  • Picture-in-Picture 모드 (선택)

6. 보안 및 최적화

  • TLS 기반 통신 필수
  • 미디어 암호화 (대부분 SDK에서 제공)
  • 배터리 소모 고려 (영상 퀄리티 자동 조절)
  • 네트워크 끊김 복구 로직 포함
  • 플랫폼별 메모리 관리

7. 테스트 환경

  • 실제 기기에서 여러 상황 테스트 (Wi-Fi, LTE, 비행기모드 전환 등)
  • 서로 다른 기기간 호환성 테스트
  • Android/iOS 모두 대응

필수 플러그인

1. camera

camera 플러그인은 모바일 기기의 전/후면 카메라 접근, 사진 및 영상 촬영, 프리뷰 화면 제공 등의 기능을 사용할 수 있게 해주는 공식 플러그인입니다.

항목 설명
이름 camera
최신 버전 0.10.x (2025년 기준)
플랫폼 Android, iOS (Web은 미지원)
주요 기능 카메라 프리뷰, 사진/영상 촬영, 카메라 전환, 플래시 제어 등

사용 시 필요한 권한

Android (AndroidManifest.xml)

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

iOS (Info.plist)

<key>NSCameraUsageDescription</key>
<string>카메라 접근이 필요합니다</string>
<key>NSMicrophoneUsageDescription</key>
<string>마이크 접근이 필요합니다</string>
  • camera 플러그인은 Flutter에서 가장 복잡한 플러그인 중 하나입니다. 다양한 기기에서 버그가 있을 수 있어요.
  • 반드시 controller.initialize()가 완료된 후에 프리뷰를 표시해야 합니다.
  • 앱 실행 중 기기가 회전하거나 상태가 바뀌면 CameraController를 재설정해야 할 수도 있습니다.
  • 영상통화 앱에서 사용하기보다는, 사진 촬영 기능이나 단순 영상 녹화 기능에 적합합니다.

영상통화에서는 camera 대신 flutter_webrtc 혹은 SDK 제공 카메라 처리를 사용하는 것이 일반적입니다.

상황 camera 사용 권장 여부
일반 사진 촬영 앱 추천
신분증 촬영, OCR 앱 추천
영상 녹화 앱 추천 (간단한 경우)
실시간 영상통화 앱 비추천 → flutter_webrtc, agora_rtc_engine 사용

2. permission_handler

permission_handler 플러그인은 카메라, 마이크, 위치, 저장소, 연락처 등 다양한 권한을 런타임에 요청하고 상태를 확인할 수 있는 대표적인 권한 관리 플러그인입니다.

영상통화 앱을 만들거나, 사진/영상 저장 기능이 있는 앱을 개발할 때 필수적으로 사용됩니다.

항목 설명
이름 permission_handler
주요 용도 런타임 권한 요청, 권한 상태 확인, 설정창 이동
지원 플랫폼 Android, iOS, macOS, Windows, Linux, Web (제한적)
대표 권한 카메라, 마이크, 위치, 저장소, 알림, Bluetooth 등

주요 기능

1. 권한 요청

var status = await Permission.camera.request();
if (status.isGranted) {
  // 권한 허용됨
} else if (status.isDenied) {
  // 권한 거부됨
}

2. 권한 상태 확인

var status = await Permission.microphone.status;
if (status.isPermanentlyDenied) {
  // 설정에서 직접 허용해야 함
}

3. 여러 권한 동시에 요청

Map<Permission, PermissionStatus> statuses = await [
  Permission.camera,
  Permission.microphone,
].request();

4. 설정 페이지로 이동

openAppSettings(); // 사용자가 수동으로 권한을 켜야 할 때

Android 설정 (AndroidManifest.xml)

예: 영상통화 앱에서 필요한 경우

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>

iOS 설정 (Info.plist)

<key>NSCameraUsageDescription</key>
<string>이 앱은 카메라를 사용합니다</string>
<key>NSMicrophoneUsageDescription</key>
<string>이 앱은 마이크를 사용합니다</string>

주요 지원 권한 종류

권한 설명
Permission.camera 카메라 접근
Permission.microphone 마이크 접근
Permission.location 위치 정보
Permission.storage 저장소 접근
Permission.notification 알림 권한
Permission.bluetooth 블루투스 사용 (iOS 13 이상 필요)
  • iOS: 특정 권한은 앱 심사에 민감하므로 실제 사용 목적이 분명해야 합니다.
  • Android 13 이상: 일부 권한 분리됨 (예: 사진, 영상, 오디오 각각 따로 요청해야 함)
  • 권한을 거부한 경우 설정 페이지로 유도하는 UX 설계가 중요합니다.

영상통화 앱에 필요한 권한 예시

Future<void> requestPermissions() async {
  var statuses = await [
    Permission.camera,
    Permission.microphone,
  ].request();

  if (statuses[Permission.camera]!.isDenied ||
      statuses[Permission.microphone]!.isDenied) {
    openAppSettings(); // 사용자가 직접 설정 열도록 안내
  }
}

3. agora_rtc_engine 

Flutter로 영상통화 앱을 만들 때 가장 많이 사용되는 플러그인 중 하나가 바로 agora_rtc_engine입니다.
Agora는 실시간 영상/음성 통화에 특화된 플랫폼이며, 높은 품질과 낮은 지연시간, 안정적인 SDK 지원이 강점입니다.

항목 설명
플러그인 이름 agora_rtc_engine
제공사 Agora
주 용도 Flutter에서 영상/음성 통화 구현
기반 기술 WebRTC 기반이지만 자체적으로 최적화
지원 플랫폼 Android, iOS, macOS, Windows, Web (패키지 분리됨)

주요 기능

기능 설명
음성/영상 통화 1:1 또는 그룹 통화 가능
채널 생성 및 참가 특정 방(채널)에 입장해서 통화
마이크/카메라 제어 ON/OFF, 전환, 볼륨 조절 등
뷰 렌더링 로컬/리모트 비디오 표시
통화 상태 이벤트 상대방 입장, 나감, 네트워크 품질 등 콜백 제공
화질 설정 저화질/고화질 전환 가능
음성 전용 모드 영상 없이 음성만도 가능
화면 공유 (지원 플랫폼 한정) 예: 데스크탑 앱에서 가능

주요 예시 코드

1. Agora 계정 생성 및 App ID 발급
https://console.agora.io

 

2. Flutter 프로젝트에 플러그인 추가

dependencies:
  agora_rtc_engine: ^6.2.0
  permission_handler: ^11.0.0

3. 필수 권한 요청

await [
  Permission.camera,
  Permission.microphone,
].request();

4. 초기화 및 채널 참가

final engine = createAgoraRtcEngine();
await engine.initialize(
  const RtcEngineContext(appId: "<YOUR_AGORA_APP_ID>"),
);
await engine.joinChannel(
  token: "<TOKEN>", 
  channelId: "testChannel",
  uid: 0, 
  options: const ChannelMediaOptions(),
);

5. 비디오 활성화 및 로컬/리모트 화면 표시

await engine.enableVideo();

engine.registerEventHandler(
  RtcEngineEventHandler(
    onUserJoined: (connection, remoteUid, elapsed) {
      print('사용자 $remoteUid 입장');
    },
  ),
);

6. 화면에 보여주기

AgoraVideoView(
  controller: VideoViewController(
    rtcEngine: engine,
    canvas: const VideoCanvas(uid: 0),
  ),
),

7. Android/iOS 설정 요약

Android (AndroidManifest.xml)

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />

iOS (Info.plist)

<key>NSCameraUsageDescription</key>
<string>카메라 접근 필요</string>
<key>NSMicrophoneUsageDescription</key>
<string>마이크 접근 필요</string>
<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>
  • 개발 초기에는 temporary token 없이도 채널 참여 가능
  • 실제 서비스에서는 Token Server 구축 필요
  • 토큰은 일정 시간 후 만료됨 → 리프레시 로직 필요
  • 무료 요금제는 월간 사용 시간 및 MAU 제한 있음
  • Web 지원은 별도 플러그인 사용 필요 (agora_uikit 등)
  • 채널 ID 충돌, 토큰 만료, 네트워크 장애 등 예외 처리 필수
항목 설명
안정성 수많은 글로벌 앱에서 사용 중 (ex. Clubhouse)
문서/가이드 풍부 공식 문서, 튜토리얼, Flutter 예제 모두 제공
영상 품질 네트워크 상황에 따라 자동 조정
확장성 채팅, 화이트보드, 녹화 등 기능 연동 가능

4. flutter_webrtc

Flutter 앱에서 WebRTC 기술을 사용해 실시간 영상통화, 음성통화, P2P 미디어 스트리밍 등을 구현할 수 있도록 해주는 오픈소스 플러그인입니다.

WebRTC(Web Real-Time Communication)는 브라우저나 모바일 앱 간에 실시간 통신(영상/음성/데이터)을 가능하게 하는 표준 기술입니다.

항목 내용
플러그인 이름 flutter_webrtc
지원 플랫폼 Android, iOS, macOS, Web, Windows, Linux
주 용도 실시간 영상통화, 음성통화, 파일 전송 등
연결 방식 P2P (기본), 필요시 SFU/MCU와 연동 가능
대표 특징 WebRTC 표준을 기반으로 구현됨, 직접 시그널링 구현 필요

주요 기능

기능 설명
카메라/마이크 캡처 로컬 미디어 스트림 생성
미디어 스트림 공유 상대방에게 영상/음성 전송
화면 표시 로컬/원격 스트림을 UI에 표시 (RTCVideoView)
데이터 채널 파일, 채팅 등 전송 가능
시그널링 처리 SDP/ICE 교환을 수동으로 처리해야 함

WebRTC에서 필요한 구성요소

  1. Media Stream
    카메라, 마이크 스트림 획득 (navigator.getUserMedia)
  2. RTCPeerConnection
    실시간 연결 객체, ICE 후보 처리, SDP 교환
  3. Signaling
    직접 구현 필요
    SDP, ICE Candidate 등을 전송할 수 있는 서버 (WebSocket, Firebase 등 사용 가능)

기본 흐름 (간단 요약)

flutter_webrtc는 로우레벨 라이브러리입니다. 시그널링 서버를 직접 구현해야 합니다.

final localRenderer = RTCVideoRenderer();
await localRenderer.initialize();

// 1. 로컬 스트림 가져오기
final stream = await navigator.mediaDevices.getUserMedia({
  'audio': true,
  'video': {'facingMode': 'user'}
});
localRenderer.srcObject = stream;

// 2. 피어 연결 생성
final peer = await createPeerConnection(configuration);
peer.addStream(stream);

// 3. SDP 생성 및 시그널링 서버를 통해 전송
final offer = await peer.createOffer();
await peer.setLocalDescription(offer);
// → 시그널링 서버로 offer 전송 필요

플랫폼 권한 설정

Android (AndroidManifest.xml)

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>

iOS (Info.plist)

<key>NSCameraUsageDescription</key>
<string>카메라 접근 필요</string>
<key>NSMicrophoneUsageDescription</key>
<string>마이크 접근 필요</string>

장점 vs 단점

장점 단점
자유도 높음 (커스터마이징 가능) X 시그널링 직접 구현해야 함
완전 무료, 오픈소스 X SDK에 비해 복잡함
Web과도 완벽 연동 가능 X 네트워크 연결/ICE 실패 처리 등 고려할 게 많음

Agora 플랫폼에 가입하고 App ID를 발급받기

Flutter에서 agora_rtc_engine을 사용하려면 Agora 플랫폼에 가입하고 App ID를 발급받아야합니다.

1. Agora 공식 홈페이지 접속

2. 회원가입 진행

이메일 또는 GitHub/Google 계정으로 가입 가능

이메일 가입:

  • 이메일 주소 입력
  • 비밀번호 설정
  • 확인 이메일 수신 후 링크 클릭하여 인증

3. Agora 콘솔 (Dashboard) 접속

  • 로그인 후 https://console.agora.io 로 이동
  • 좌측 메뉴에서 "Project Management" (프로젝트 관리) 클릭

4. 새 프로젝트 생성

  1. "Create" 또는 "+ New Project" 클릭
  2. 입력 정보:
    Project Name: 예) FlutterVideoCall
    Authentication Method:
      테스트용이면: APP ID 만 사용 (간단)
      실서비스용이면: APP ID + Token 사용 권장 (보안 강화)
  3. 생성 완료 후 App ID가 발급됨

5. App ID 확인 및 복사

  • 생성된 프로젝트 목록에서 원하는 프로젝트 클릭
  • 상단에 있는 App ID 복사 버튼 클릭해서 Flutter 프로젝트에 붙여 넣기
728x90

영상통화 앱 만들기

권한 설정

영상통화 앱은  Android+windows 앱을 대상으로 작성되었습니다.

Flutter로 agora_rtc_engine, permission_handler 플러그인을 사용해서 영상통화 앱을 개발할때 아래와 같은 권한설정이 필요합니다.

Android 권한 설정 (android/app/src/main/AndroidManifest.xml)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kr.co.xxxxxx.mobile.andrew_video_call">

    <!-- 🔐 필요한 권한들 -->
    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>

    <!-- Android 13 (TIRAMISU) 이상부터는 추가 -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />

Android 10 이상일 때 고려사항

  • CAMERA와 RECORD_AUDIO는 런타임 권한 요청 필수
  • permission_handler 플러그인을 통해 런타임 권한 요청 코드 필요
  • Android 13 이상부터는 알림 권한(POST_NOTIFICATIONS) 도 있어야 영상통화 수신 알림 가능
권한 설명
INTERNET 네트워크 사용 (Agora 등)
CAMERA 전면/후면 카메라 사용
RECORD_AUDIO 마이크 사용
MODIFY_AUDIO_SETTINGS 오디오 설정 변경 권한 (선택적)
BLUETOOTH / BLUETOOTH_ADMIN 일부 기기에서 필요할 수 있음
ACCESS_NETWORK_STATE 네트워크 상태 확인
WAKE_LOCK 통화 중 화면 꺼짐 방지
POST_NOTIFICATIONS Android 13+ 알림 허용
BLUETOOTH_CONNECT / NEARBY_WIFI_DEVICES Android 12+ 일부 통신 권한

권한 요청 예시 (Flutter 코드)

Future<void> requestPermissions() async {
  final statuses = await [
    Permission.camera,
    Permission.microphone,
    Permission.bluetooth, // Android 12+
    Permission.notification, // Android 13+
  ].request();

  if (statuses.values.any((status) => status.isDenied)) {
    openAppSettings(); // 권한 직접 허용하라고 유도
  }
}

Video Call 예제 코드

첫 화면에는 로고 + 버튼, 버튼 클릭 시 영상채팅방에 입장하는 구조입니다.

1. 의존성 추가 (pubspec.yaml)

dependencies:
  flutter:
    sdk: flutter
  agora_rtc_engine: ^6.2.0
  permission_handler: ^11.0.0

2. 전체 구조

lib/
 ├── main.dart            ← 첫 화면 + 채팅방 이동
 ├── screen/video_call_page.dart ← 영상채팅방 UI

3. main.dart

// Flutter의 UI 구성 및 앱 실행에 필요한 패키지
import 'package:flutter/material.dart';
// 영상통화 화면을 불러오기 위한 import (사용자 정의 페이지)
import 'package:andrew_video_call/screen/video_call_page.dart';

// 앱 시작점 (main 함수) → runApp으로 앱 전체를 실행
void main() {
  runApp(const VideoCallApp());
}

// 앱 전체를 감싸는 위젯 (StatelessWidget 사용)
class VideoCallApp extends StatelessWidget {
  const VideoCallApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Agora Video Call',      // 앱 타이틀 (디버깅/최근 앱 목록 등)
      theme: ThemeData.dark(),        // 전체 테마를 다크모드로 설정
      home: const HomeScreen(),       // 앱 첫 화면은 HomeScreen 위젯으로 지정
    );
  }
}

// 영상통화 진입 버튼을 제공하는 첫 화면
class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center( // 화면 중앙에 콘텐츠 배치
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center, // 세로 방향으로 가운데 정렬
          children: [
            // 비디오 통화 아이콘 (앱 로고 느낌)
            const Icon(Icons.video_call, size: 100, color: Colors.blueAccent),

            const SizedBox(height: 40), // 아이콘과 버튼 사이 여백

            // 채팅방 입장 버튼
            ElevatedButton(
              onPressed: () {
                // 버튼 클릭 시 영상통화 화면으로 이동 (페이지 전환)
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (_) => const VideoCallPage()),
                );
              },
              child: const Text("채팅방 들어가기"), // 버튼 텍스트
            ),
          ],
        ),
      ),
    );
  }
}

위치 설명
main() 앱 실행 시작점
VideoCallApp 전체 앱 테마 및 구조 정의
HomeScreen 아이콘 + 버튼 UI 구성
ElevatedButton.onPressed 버튼 클릭 시 VideoCallPage로 화면 전환

Main 화면(PC 실행)

4. video_call_page.dart

// 필수 패키지 임포트
import 'package:agora_rtc_engine/agora_rtc_engine.dart'; // 아고라 실시간 통신 SDK
import 'package:flutter/material.dart'; // Flutter UI 위젯
import 'package:permission_handler/permission_handler.dart'; // 런타임 권한 요청 패키지

// 아고라 프로젝트에서 발급받은 고유 App ID
const String appId = 'ff3d53c4637b4ae0974b24ef4f98d3fe';

// 통화에 사용할 채널 이름 (모든 참가자가 동일한 채널명을 써야 연결됨)
const String channelName = 'flutter_call';

// 토큰 인증값 (콘솔에서 발급 / App Certificate 활성화 시 필수)
const String token = '007eJxTYFD7PUnKc33MK13...'; // 보안을 위해 실제 배포 시 숨겨야 함

// 영상 통화 화면의 Stateful 위젯 정의
class VideoCallPage extends StatefulWidget {
  const VideoCallPage({super.key});

  @override
  State<VideoCallPage> createState() => _VideoCallPageState();
}

class _VideoCallPageState extends State<VideoCallPage> {
  late RtcEngine _engine; // Agora 엔진 인스턴스
  int? _remoteUid;        // 상대방 UID
  bool _isJoined = false; // 내가 채널에 성공적으로 입장했는지 여부

  @override
  void initState() {
    super.initState();
    _initAgora(); // 위젯 초기화 시 아고라 초기 설정 실행
  }

  // 아고라 엔진 초기화 + 권한 요청 + 채널 입장
  Future<void> _initAgora() async {
    // 마이크 및 카메라 권한 요청
    await [Permission.camera, Permission.microphone].request();

    // 아고라 엔진 생성 및 초기화
    _engine = createAgoraRtcEngine();
    await _engine.initialize(
      RtcEngineContext(
        appId: appId,
        channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, // 방송용 채널 모드 사용
      ),
    );

    // 이벤트 핸들러 등록
    _engine.registerEventHandler(RtcEngineEventHandler(
      // 내가 채널에 입장했을 때 호출됨
      onJoinChannelSuccess: (conn, elapsed) {
        setState(() {
          this._isJoined = true;
        });
      },
      // 다른 사용자가 입장했을 때 호출됨
      onUserJoined: (conn, uid, elapsed) {
        setState(() {
          _remoteUid = uid;
        });
      },
      // 상대방이 나갔을 때 호출됨
      onUserOffline: (conn, uid, reason) {
        setState(() {
          _remoteUid = null;
        });
      },
    ));

    // 내 역할을 방송자(Broadcaster)로 설정 (영상 송출자)
    await _engine.setClientRole(role: ClientRoleType.clientRoleBroadcaster);

    // 영상 사용 활성화
    await _engine.enableVideo();

    // 로컬 영상 미리보기 시작 (채널 입장 전 카메라 미리보기)
    await _engine.startPreview();

    // 채널에 입장 (UID는 0으로 설정하면 Agora가 자동으로 UID 할당)
    await _engine.joinChannel(
      token: token,
      channelId: channelName,
      uid: 0, // 자동 UID
      options: ChannelMediaOptions(), // 입장 옵션 (기본값 사용)
    );
  }

  @override
  void dispose() {
    // 채널에서 나가기 및 엔진 해제
    _engine.leaveChannel();
    _engine.release();
    super.dispose();
  }

  // 내 영상 프리뷰 렌더링
  Widget _renderLocalPreview() {
    return AgoraVideoView(
      controller: VideoViewController(
        rtcEngine: _engine,
        canvas: const VideoCanvas(uid: 0), // 내 UID (0 또는 자동 할당 UID)
      ),
    );
  }

  // 상대방 영상 렌더링
  Widget _renderRemoteVideo() {
    if (_remoteUid != null) {
      return AgoraVideoView(
        controller: VideoViewController.remote(
          rtcEngine: _engine,
          canvas: VideoCanvas(uid: _remoteUid!), // 상대방 UID
          connection: const RtcConnection(channelId: channelName),
        ),
      );
    } else {
      // 상대방이 아직 입장하지 않음
      return Center(
        child: Text("상대방 입장 대기 중...", style: TextStyle(color: Colors.white)),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("영상 채팅방")),
      body: _isJoined
          ? Stack(
        children: [
          _renderRemoteVideo(), // 전체 화면에 상대방 영상
          Positioned(
            top: 16,
            right: 16,
            width: 120,
            height: 160,
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(color: Colors.white),
              ),
              child: _renderLocalPreview(), // 오른쪽 상단에 내 영상
            ),
          )
        ],
      )
          : const Center(child: CircularProgressIndicator()), // 입장 중 로딩 표시
    );
  }
}
기능 위치 설명
권한 요청 _initAgora() 카메라, 마이크 권한 요청
엔진 초기화 _initAgora() Agora 엔진 설정 및 역할 설정
채널 입장 _initAgora() joinChannel() 호출
이벤트 처리 registerEventHandler 상대방 입장/퇴장 감지
UI 렌더링 _renderLocalPreview, _renderRemoteVideo 로컬/리모트 영상 표시
종료 처리 dispose() 채널 종료 및 엔진 해제

Android 실행화면

728x90