Flutter 앱 개발에 있어 카메라 기능은 중요한 요소 중 하나입니다. 이번 글에서는 camera 패키지를 사용하여 플러터 카메라 연동 방법과 사용자가 사진을 찍은 후 이를 화면에 표시하는 예제에 대해서 작성합니다.
플러터 카메라 연동 예제 목차
camera 패키지 추가하기
Flutter 프로젝트에 카메라 기능을 추가하려면 먼저 camera 패키지를 프로젝트에 포함시켜야 합니다. 이를 위해 프로젝트의 pubspec.yaml 파일을 열고, dependencies에 다음 줄을 추가합니다
dependencies: flutter: sdk: flutter camera: ^0.9.4+5
이 코드는 camera 패키지의 최신 버전을 의존성에 추가합니다. 변경사항을 저장한 후 터미널에서 flutter pub get 명령어를 실행하여 패키지를 설치하세요.
안드로이드 개발자 모드
실기기에 연결하여 카메라를 사용하기 위해 안드로이드 기기에서 개발자 모드로 변경해야됩니다.
기기마다 다르나 설정 > 시스템 > 휴대전화 정보, 빌드번호등을 여러번 클릭하여 개발자모드로 변경합니다.
카메라 사용 권한 설정하기
안드로이드와 iOS에서 카메라를 사용하기 위해서는 각 플랫폼의 설정 파일에서 카메라 사용 권한을 추가해야 합니다.
Android: android/app/src/main/AndroidManifest.xml 파일을 열고 태그를 추가합니다:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.yourapp"> <uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera" android:required="true"/> <uses-feature android:name="android.hardware.camera.autofocus"/> <application ... </application> </manifest>
iOS: ios/Runner/Info.plist 파일을 열고 다음 키를 추가합니다:
<key>NSCameraUsageDescription</key> <string>카메라 접근 허용</string>
카메라 연동 예제 코드
Flutter 앱에 카메라 기능을 통합하기 위해 main.dart 파일을 다음과 같이 수정합니다:
메인 함수
앱이 시작될 때 Flutter 엔진과 위젯 트리가 초기화되도록 합니다. 비동기 작업을 main 함수에서 수행하기 전에 필요한 호출입니다. availableCameras()으로 사용 가능한 카메라 목록을 비동기적으로 불러옵니다.
// 필요한 패키지들을 임포트합니다. import 'package:flutter/material.dart'; import 'package:camera/camera.dart'; import 'dart:io'; Future<void> main() async { // Flutter 엔진과 위젯 트리의 초기화를 보장합니다. WidgetsFlutterBinding.ensureInitialized(); // 사용 가능한 카메라 리스트를 비동기로 가져옵니다. final cameras = await availableCameras(); // 가져온 카메라 리스트에서 첫 번째 카메라를 선택합니다. final firstCamera = cameras.first; // 앱을 실행합니다. MaterialApp을 사용하여 앱의 루트 위젯을 정의합니다. runApp(MaterialApp( theme: ThemeData.dark(), // 앱의 전반적인 테마를 다크 테마로 설정합니다. home: TakePictureScreen(camera: firstCamera), // 홈 스크린으로 TakePictureScreen을 설정하고, 선택된 카메라를 전달합니다. )); }
사진 촬영 위젯
TakePictureScreen은 사용자가 사진을 캡처할 수 있는 화면입니다. 카메라 컨트롤러(CameraController)를 통해 카메라의 상태와 스트림을 관리합니다. FloatingActionButton을 누르면 onPressed 콜백에서 사진을 캡처하고, 이를 DisplayPictureScreen으로 전달합니다.
// 사진 촬영 화면을 담당할 StatefulWidget입니다. class TakePictureScreen extends StatefulWidget { final CameraDescription camera; // 생성자를 통해 카메라 객체를 전달받습니다. 필수로 전달받아야 하므로 @required로 표시합니다. const TakePictureScreen({Key? key, required this.camera}) : super(key: key); @override _TakePictureScreenState createState() => _TakePictureScreenState(); } // TakePictureScreen의 상태를 관리하는 클래스입니다. class _TakePictureScreenState extends State<TakePictureScreen> { late CameraController _controller; // 카메라 컨트롤러 late Future<void> _initializeControllerFuture; // 컨트롤러 초기화 작업을 담당할 Future 객체 @override void initState() { super.initState(); // CameraController 인스턴스를 생성합니다. 카메라 설정은 해상도를 중간으로 설정합니다. _controller = CameraController( widget.camera, ResolutionPreset.medium, ); // 컨트롤러의 초기화를 비동기로 진행합니다. _initializeControllerFuture = _controller.initialize(); } @override void dispose() { // 위젯이 dispose될 때, 카메라 컨트롤러도 dispose하여 리소스를 해제합니다. _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('카메라 예제')), // 앱바 제목 설정 // 비동기 작업을 다루기 위해 FutureBuilder를 사용합니다. body: FutureBuilder<void>( future: _initializeControllerFuture, builder: (context, snapshot) { // Future가 완료되면, 즉 카메라 초기화가 끝나면 카메라 미리보기를 보여줍니다. if (snapshot.connectionState == ConnectionState.done) { return CameraPreview(_controller); // 카메라 미리보기 위젯 } else { // 아직 로딩 중이라면 로딩 인디케이터를 보여줍니다. return Center(child: CircularProgressIndicator()); } }, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.camera_alt), // 버튼이 눌리면 사진을 캡처하는 기능을 수행합니다. onPressed: () async { try { // 컨트롤러 초기화를 기다립니다. await _initializeControllerFuture; // 사진 캡처를 시도하고 결과를 저장합니다. final image = await _controller.takePicture(); // 캡처된 사진을 보여주는 화면으로 이동합니다. if (!mounted) return; await Navigator.of(context).push( MaterialPageRoute( builder: (context) => DisplayPictureScreen(imagePath: image.path), ), ); } catch (e) { print(e); // 에러가 발생한 경우 콘솔에 에러를 출력합니다. } }, ), ); } }
사진 확인 위젯
생성자를 통해 전달받은 imagePath를 사용해 Image.file 위젯으로 사진을 로드하고 표시합니다. body에 캡쳐한 사진을 보여줍니다.
// 캡처된 사진을 보여주는 StatelessWidget입니다. class DisplayPictureScreen extends StatelessWidget { final String imagePath; // 생성자를 통해 이미지 경로를 전달받습니다. const DisplayPictureScreen({Key? key, required this.imagePath}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('사진 보기')), // 앱바 제목 설정 // 파일 시스템에서 이미지를 로드하여 화면에 표시합니다. body: Image.file(File(imagePath)), ); } }