티스토리 뷰

Flutter는 다양한 플러그인을 제공하여 모바일 개발을 더욱 쉽고 강력하게 만들어줍니다. 그중 하나가 카메라 플러그인으로, 이를 사용하면 간단히 앱에서 사진을 찍거나 동영상을 녹화할 수 있습니다. 이 블로그에서는 Flutter Camera 플러그인을 사용해 카메라 기능을 구현하는 방법을 단계별로 알아보겠습니다.

참고. Take a picture using the camera

왜 Flutter Camera Plugin을 사용해야 할까?

Flutter Camera Plugin은 다음과 같은 강력한 기능을 제공합니다:

  • 사진 촬영: 앱 내에서 고품질의 이미지를 캡처 가능.
  • 동영상 녹화: 동영상 촬영과 저장을 위한 API 제공.
  • 다양한 플랫폼 지원: Android와 iOS 모두에서 동일한 코드로 구현 가능.
  • 다양한 설정 옵션: 카메라 해상도, 플래시 모드, 줌 설정 등 세부 조정 가능.

Flutter Camera Plugin 설정 방법

1. 플러그인 설치

Flutter 프로젝트에서 camera 플러그인을 추가합니다. 프로젝트의 pubspec.yaml 파일에 다음을 추가하세요:

dependencies:
  camera: ^0.10.0+4

이후 flutter pub get 명령어를 실행해 플러그인을 설치합니다.

2. Android 및 iOS 권한 설정

  • 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>

3. 초기화 및 카메라 컨트롤러 설정

카메라를 사용하려면 CameraController를 초기화해야 합니다. 다음은 기본적인 초기화 코드입니다:

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

class CameraExample extends StatefulWidget {
  @override
  _CameraExampleState createState() => _CameraExampleState();
}

class _CameraExampleState extends State<CameraExample> {
  late CameraController _controller;
  late List<CameraDescription> _cameras;

  @override
  void initState() {
    super.initState();
    _initializeCamera();
  }

  Future<void> _initializeCamera() async {
    _cameras = await availableCameras();
    _controller = CameraController(
      _cameras[0], // 후면 카메라 선택
      ResolutionPreset.high,
    );
    await _controller.initialize();
    setState(() {});
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (!_controller.value.isInitialized) {
      return Center(child: CircularProgressIndicator());
    }
    return CameraPreview(_controller);
  }
}

Flutter로 사진 촬영하기

사진을 촬영하려면 CameraController.takePicture() 메서드를 사용합니다. 다음은 버튼 클릭으로 사진을 촬영하고 저장하는 예제입니다:

Future<void> _takePicture() async {
  try {
    final image = await _controller.takePicture();
    print('사진 저장 경로: ${image.path}');
  } catch (e) {
    print('사진 촬영 실패: $e');
  }
}

사용자 경험을 위한 추가 기능

1. 플래시 모드 변경

await _controller.setFlashMode(FlashMode.auto);

2. 줌 기능 추가

await _controller.setZoomLevel(2.0);

3. 동영상 녹화

await _controller.startVideoRecording();
await _controller.stopVideoRecording();

주요 고려사항

  • 권한 요청: Android와 iOS의 권한 요청은 사용자가 앱을 처음 실행할 때 꼭 확인해야 합니다.
  • 디바이스 호환성: 모든 디바이스가 동일한 카메라 설정을 지원하지 않을 수 있습니다.
  • 리소스 관리: 카메라 컨트롤러는 사용 후 반드시 dispose()를 호출해 리소스를 해제해야 합니다.

Flutter Camera Plugin으로 확장 가능한 프로젝트 아이디어

  1. QR 코드 스캐너: 사진 촬영 대신 실시간 QR 코드 스캔 기능 구현.
  2. SNS 카메라 앱: 필터와 스티커를 추가해 사진 편집 가능.
  3. 동영상 편집 앱: 녹화한 동영상에 간단한 편집 기능 추가.

결론

Flutter Camera Plugin은 Flutter 개발자들에게 강력한 도구를 제공합니다. 이 플러그인을 활용하면 단순한 사진 촬영 앱부터 복잡한 멀티미디어 애플리케이션까지 다양한 기능을 손쉽게 구현할 수 있습니다. 위 가이드를 참고하여 여러분만의 독창적인 카메라 애플리케이션을 만들어 보세요!

 

import 'dart:async';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
  // Ensure that plugin services are initialized so that `availableCameras()`
  // can be called before `runApp()`
  WidgetsFlutterBinding.ensureInitialized();

  // Obtain a list of the available cameras on the device.
  final cameras = await availableCameras();

  // Get a specific camera from the list of available cameras.
  final firstCamera = cameras.first;

  runApp(
    MaterialApp(
      theme: ThemeData.dark(),
      home: TakePictureScreen(
        // Pass the appropriate camera to the TakePictureScreen widget.
        camera: firstCamera,
      ),
    ),
  );
}

// A screen that allows users to take a picture using a given camera.
class TakePictureScreen extends StatefulWidget {
  const TakePictureScreen({
    super.key,
    required this.camera,
  });

  final CameraDescription camera;

  @override
  TakePictureScreenState createState() => TakePictureScreenState();
}

class TakePictureScreenState extends State<TakePictureScreen> {
  late CameraController _controller;
  late Future<void> _initializeControllerFuture;

  @override
  void initState() {
    super.initState();
    // To display the current output from the Camera,
    // create a CameraController.
    _controller = CameraController(
      // Get a specific camera from the list of available cameras.
      widget.camera,
      // Define the resolution to use.
      ResolutionPreset.medium,
    );

    // Next, initialize the controller. This returns a Future.
    _initializeControllerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    // Dispose of the controller when the widget is disposed.
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Take a picture')),
      // You must wait until the controller is initialized before displaying the
      // camera preview. Use a FutureBuilder to display a loading spinner until the
      // controller has finished initializing.
      body: FutureBuilder<void>(
        future: _initializeControllerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            // If the Future is complete, display the preview.
            return CameraPreview(_controller);
          } else {
            // Otherwise, display a loading indicator.
            return const Center(child: CircularProgressIndicator());
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        // Provide an onPressed callback.
        onPressed: () async {
          // Take the Picture in a try / catch block. If anything goes wrong,
          // catch the error.
          try {
            // Ensure that the camera is initialized.
            await _initializeControllerFuture;

            // Attempt to take a picture and get the file `image`
            // where it was saved.
            final image = await _controller.takePicture();

            if (!context.mounted) return;

            // If the picture was taken, display it on a new screen.
            await Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => DisplayPictureScreen(
                  // Pass the automatically generated path to
                  // the DisplayPictureScreen widget.
                  imagePath: image.path,
                ),
              ),
            );
          } catch (e) {
            // If an error occurs, log the error to the console.
            print(e);
          }
        },
        child: const Icon(Icons.camera_alt),
      ),
    );
  }
}

// A widget that displays the picture taken by the user.
class DisplayPictureScreen extends StatelessWidget {
  final String imagePath;

  const DisplayPictureScreen({super.key, required this.imagePath});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Display the Picture')),
      // The image is stored as a file on the device. Use the `Image.file`
      // constructor with the given path to display the image.
      body: Image.file(File(imagePath)),
    );
  }
}