import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
// 비디오 녹화 화면을 위한 StatefulWidget
class VideoRecordingScreen extends StatefulWidget {
const VideoRecordingScreen({super.key});
State<VideoRecordingScreen> createState() => _VideoRecordingScreenState();
}
class _VideoRecordingScreenState extends State<VideoRecordingScreen> {
bool _hasPermission = false; // 카메라 및 마이크 권한 보유 여부
bool _isSelfieMode = false; // 셀피 모드 활성화 여부
late CameraController _cameraController; // 카메라 제어를 위한 컨트롤러
// 카메라 초기화 메서드
Future<void> initCamera() async {
final cameras = await availableCameras(); // 사용 가능한 카메라 목록 조회
if (cameras.isEmpty) {
// 사용 가능한 카메라가 없는 경우 초기화 종료
return;
}
// 셀피 모드 여부에 따라 사용할 카메라 선택
_cameraController = CameraController(
cameras[_isSelfieMode ? 1 : 0], // 셀피 모드면 두 번째 카메라(일반적으로 전면), 아니면 첫 번째 카메라(일반적으로 후면)
ResolutionPreset.ultraHigh,
);
await _cameraController.initialize(); // 카메라 컨트롤러 초기화
}
// 권한 초기화 메서드
Future<void> initPermissions() async {
final cameraPermission = await Permission.camera.request(); // 카메라 권한 요청
final micPermission = await Permission.microphone.request(); // 마이크 권한 요청
// 권한 거부 여부 확인
final cameraDenied =
cameraPermission.isDenied || cameraPermission.isPermanentlyDenied;
final micDenied =
micPermission.isDenied || micPermission.isPermanentlyDenied;
if (!cameraDenied && !micDenied) {
// 모든 권한이 허용된 경우
_hasPermission = true;
await initCamera(); // 카메라 초기화
setState(() {}); // UI 업데이트를 위한 상태 변경
}
}
void initState() {
super.initState();
initPermissions(); // 권한 및 카메라 초기화 시작
}
// 셀피 모드 전환 메서드
Future<void> _toggleSelfieMode() async {
_isSelfieMode = !_isSelfieMode; // 셀피 모드 상태 전환
await initCamera(); // 카메라 재초기화
setState(() {}); // UI 업데이트
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SizedBox(
width: MediaQuery.of(context).size.width,
child: !_hasPermission || !_cameraController.value.isInitialized
? Column(
// 권한이 없거나 카메라가 초기화되지 않은 경우 로딩 화면 표시
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text("Initializing...", style: TextStyle(color: Colors.white, fontSize: Sizes.size20)),
Gaps.v20,
CircularProgressIndicator.adaptive()
],
)
: Stack(
// 카메라 미리보기와 셀피 모드 전환 버튼 표시
alignment: Alignment.center,
children: [
CameraPreview(_cameraController),
Positioned(
top: Sizes.size20,
left: Sizes.size20,
child: IconButton(
color: Colors.white,
onPressed: _toggleSelfieMode, // 셀피 모드 전환 버튼 액션
icon: const Icon(Icons.cameraswitch),
),
),
],
),
),
);
}
}
이 코드는 Flutter 앱에서 카메라를 사용하여 비디오 녹화 화면을 구현하는 VideoRecordingScreen
클래스입니다. 이 클래스는 카메라와 마이크 권한을 요청하고, 권한이 허용되면 카메라를 초기화하여 사용자에게 카메라 미리보기를 제공합니다.
initPermissions
메서드를 통해 카메라와 마이크 권한을 사용자에게 요청합니다. 사용자가 권한을 거부하면, 카메라 사용이 불가능합니다.initCamera
메서드를 호출하여 사용 가능한 카메라 목록을 가져오고, 첫 번째 카메라를 선택하여 CameraController
를 사용해 초기화합니다.CameraPreview
위젯을 사용하여 카메라 미리보기를 화면에 표시합니다. 권한이 거부되거나 카메라 초기화가 완료되지 않은 상태에서는 "Initializing..." 텍스트와 함께 로딩 인디케이터를 표시합니다.initPermissions
메서드에서는 permission_handler
패키지를 사용하여 카메라와 마이크 권한을 요청하고, 권한 상태를 확인합니다. 모든 권한이 허용되면 _hasPermission
을 true
로 설정하고 initCamera
메서드를 호출합니다.initCamera
메서드에서는 camera
패키지의 availableCameras
함수를 사용해 사용 가능한 카메라 목록을 가져옵니다. 선택된 카메라로 CameraController
를 생성하고 초기화합니다.build
메서드에서는 화면에 카메라 미리보기(CameraPreview
)를 표시하거나, 권한 요청/카메라 초기화가 진행 중일 때 로딩 화면을 표시합니다.이 코드를 통해 Flutter 앱에서 카메라를 사용하는 기본적인 방법을 이해할 수 있으며, 추가적인 카메라 기능 구현 시 이를 기반으로 확장할 수 있습니다.
위 코드의 비즈니스 로직은 크게 세 부분으로 나뉩니다: 권한 요청, 카메라 초기화, 그리고 UI 렌더링입니다. 각 단계의 흐름을 세부적으로 살펴보겠습니다.
initPermissions
메서드)initCamera
)를 호출하고, _hasPermission
플래그를 true
로 설정합니다. 이 플래그는 카메라 사용 가능 여부를 UI에 알려줍니다.initCamera
메서드)availableCameras
함수를 호출하여 장치에서 사용 가능한 카메라 목록을 가져옵니다.CameraController
를 사용하여 해당 카메라를 초기화합니다. 여기서 해상도는 ResolutionPreset.ultraHigh
로 설정됩니다._cameraController.initialize()
호출을 통해 카메라가 사용 준비가 완료되면, UI를 업데이트하기 위해 setState
를 호출합니다.build
메서드)_hasPermission
과 _cameraController.value.isInitialized
를 확인하여, 권한이 허용되었고 카메라 초기화가 완료되었는지 판단합니다.CameraPreview
위젯을 사용하여 화면에 카메라 미리보기를 표시합니다.이 로직은 사용자에게 필요한 권한을 요청하고, 권한이 허용된 후에는 카메라를 초기화하여 카메라 미리보기를 제공합니다. 사용자가 권한을 거부하거나 카메라 초기화에 실패한 경우에는 적절한 피드백(로딩 화면)을 제공합니다. 이 과정을 통해 사용자는 앱에서 비디오 녹화 기능을 원활하게 사용할 수 있습니다.
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
class VideoRecordingScreen extends StatefulWidget {
const VideoRecordingScreen({super.key});
State<VideoRecordingScreen> createState() => _VideoRecordingScreenState();
}
class _VideoRecordingScreenState extends State<VideoRecordingScreen> {
bool _hasPermission = false;
bool _isSelfieMode = false;
late FlashMode _flashMode; // 현재 플래시 모드를 저장하는 변수
late CameraController _cameraController;
// 카메라 초기화 함수
Future<void> initCamera() async {
final cameras = await availableCameras();
if (cameras.isEmpty) {
return;
}
_cameraController = CameraController(
cameras[_isSelfieMode ? 1 : 0], // 셀피 모드에 따라 카메라 선택
ResolutionPreset.ultraHigh,
);
await _cameraController.initialize();
_flashMode = _cameraController.value.flashMode; // 초기 플래시 모드 설정
}
// 권한 확인 및 요청 함수
Future<void> initPermissions() async {
final cameraPermission = await Permission.camera.request();
final micPermission = await Permission.microphone.request();
final cameraDenied =
cameraPermission.isDenied || cameraPermission.isPermanentlyDenied;
final micDenied =
micPermission.isDenied || micPermission.isPermanentlyDenied;
if (!cameraDenied && !micDenied) {
_hasPermission = true;
await initCamera();
setState(() {});
}
}
void initState() {
super.initState();
initPermissions();
}
// 셀피 모드 전환 함수
Future<void> _toggleSelfieMode() async {
_isSelfieMode = !_isSelfieMode;
await initCamera();
setState(() {});
}
// 플래시 모드 설정 함수
Future<void> _setFlashMode(FlashMode newFlashMode) async {
await _cameraController.setFlashMode(newFlashMode); // 카메라 컨트롤러를 사용하여 플래시 모드 설정
_flashMode = newFlashMode; // 현재 플래시 모드 업데이트
setState(() {}); // UI 업데이트
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SizedBox(
width: MediaQuery.of(context).size.width,
child: !_hasPermission || !_cameraController.value.isInitialized
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text("Initializing...", style: TextStyle(color: Colors.white, fontSize: Sizes.size20)),
Gaps.v20,
CircularProgressIndicator.adaptive()
],
)
: Stack(
alignment: Alignment.center,
children: [
CameraPreview(_cameraController),
Positioned(
top: Sizes.size20,
right: Sizes.size20,
child: Column(
children: [
IconButton(
color: Colors.white,
onPressed: _toggleSelfieMode,
icon: const Icon(Icons.cameraswitch),
),
Gaps.v10,
// 플래시 모드 설정 버튼들
IconButton(
color: _flashMode == FlashMode.off ? Colors.amber.shade200 : Colors.white,
onPressed: () => _setFlashMode(FlashMode.off),
icon: const Icon(Icons.flash_off_rounded),
),
Gaps.v10,
IconButton(
color: _flashMode == FlashMode.always ? Colors.amber.shade200 : Colors.white,
onPressed: () => _setFlashMode(FlashMode.always),
icon: const Icon(Icons.flash_on_rounded),
),
Gaps.v10,
IconButton(
color: _flashMode == FlashMode.auto ? Colors.amber.shade200 : Colors.white,
onPressed: () => _setFlashMode(FlashMode.auto),
icon: const Icon(Icons.flash_auto_rounded),
),
Gaps.v10,
IconButton(
color: _flashMode == FlashMode.torch ? Colors.amber.shade200 : Colors.white,
onPressed: () => _setFlashMode(FlashMode.torch),
icon: const Icon(Icons.flashlight_on_rounded),
),
],
),
),
],
),
),
);
}
}
이 코드는 Flutter 앱에서 camera
패키지를 사용하여 비디오 녹화 화면을 구현하고, 사용자가 카메라의 플래시 모드를 조절할 수 있도록 하는 기능을 추가한 VideoRecordingScreen
클래스입니다. 사용자는 플래시 모드를 변경할 수 있는 여러 버튼을 통해 플래시를 제어할 수 있습니다.
_flashMode
변수에 저장합니다. 이는 카메라 세션 동안 사용자가 선택한 플래시 모드를 기억하기 위함입니다._setFlashMode
함수가 호출되어 새 플래시 모드로 설정됩니다. 설정 후, _flashMode
변수가 업데이트되고 UI가 다시 렌더링됩니다.카메라 미리보기: _cameraController
를 사용하여 화면에 카메라 미리보기를 표시합니다. 사용자가 플래시 모드를 변경할 수 있도록 상단 우측에 플래시 관련 버튼이 배치됩니다.
플래시 모드 버튼: 플래시 모드를 변경하는 버튼들은 현재 _flashMode
상태에 따라 색상이 변화합니다. 각 모드(꺼짐, 항상 켜짐, 자동, 토치)에 대응하는 버튼을 탭하면, 해당 모드로 플래시가 설정됩니다.
_cameraController
를 통해 카메라의 플래시 모드를 설정합니다. 설정이 완료되면, UI가 업데이트되어 사용자가 현재 선택한 플래시 모드를 시각적으로 확인할 수 있습니다.FlashMode.off
: 플래시를 사용하지 않음.FlashMode.always
: 사진 촬영 시 항상 플래시를 사용.FlashMode.auto
: 환경에 따라 자동으로 플래시를 사용할지 결정.FlashMode.torch
: 비디오 녹화 시 플래시를 지속적으로 켜둠(손전등 모드).이 코드를 통해 사용자는 비디오 녹화 시 다양한 플래시 모드 중에서 선택할 수 있게 되며, 이는 특히 어두운 환경에서 녹화할 때 유용합니다. 사용자 경험을 향상시키고, 앱의 기능성을 높이는 중요한 기능입니다.