플러터 카메라 연동 방법 및 예제

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)),
    );
  }
}

Leave a Comment