영상통화에 어플리케이션 개발시 고려할 기술적 요소들
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. 필수 플러그인
- camera: 로컬 카메라 접근
- permission_handler: 마이크, 카메라 권한 요청
- agora_rtc_engine: Agora SDK 사용 시 필요
- flutter_webrtc: WebRTC 기반 오픈소스 구현
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에서 필요한 구성요소
- Media Stream
카메라, 마이크 스트림 획득 (navigator.getUserMedia) - RTCPeerConnection
실시간 연결 객체, ICE 후보 처리, SDP 교환 - 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 공식 홈페이지 접속
- 주소: https://www.agora.io
- 오른쪽 상단의 "Sign Up" 또는 "Start for Free" 클릭
2. 회원가입 진행
이메일 또는 GitHub/Google 계정으로 가입 가능
이메일 가입:
- 이메일 주소 입력
- 비밀번호 설정
- 확인 이메일 수신 후 링크 클릭하여 인증
3. Agora 콘솔 (Dashboard) 접속
- 로그인 후 https://console.agora.io 로 이동
- 좌측 메뉴에서 "Project Management" (프로젝트 관리) 클릭
4. 새 프로젝트 생성
- "Create" 또는 "+ New Project" 클릭
- 입력 정보:
Project Name: 예) FlutterVideoCall
Authentication Method:
테스트용이면: APP ID 만 사용 (간단)
실서비스용이면: APP ID + Token 사용 권장 (보안 강화) - 생성 완료 후 App ID가 발급됨
5. App ID 확인 및 복사
- 생성된 프로젝트 목록에서 원하는 프로젝트 클릭
- 상단에 있는 App ID 복사 버튼 클릭해서 Flutter 프로젝트에 붙여 넣기
영상통화 앱 만들기
권한 설정
영상통화 앱은 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로 화면 전환 |
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() | 채널 종료 및 엔진 해제 |
'Flutter for Beginners' 카테고리의 다른 글
Flutter 포토 스티커 (0) | 2025.04.21 |
---|---|
Flutter Google Map (0) | 2025.04.18 |
Flutter 비디오 플레이어 (0) | 2025.04.10 |
Flutter 디지털 시계에 주사위 굴리기 추가 (0) | 2025.04.03 |
Flutter 디지털 시계 + 나침반 + 날씨정보 (0) | 2025.03.25 |