Study Record

[Flutter] 이미지, 비디오 파일 가져오기(image_picker) 본문

Flutter

[Flutter] 이미지, 비디오 파일 가져오기(image_picker)

초코초코초코 2023. 2. 15. 17:20
728x90

🎁 image_picker

https://pub.dev/packages/image_picker#handling-mainactivity-destruction-on-android

 

image_picker | Flutter Package

Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera.

pub.dev

 

갤러리에서 파일을 가져오거나 직접 카메라를 이용해 사진, 비디오를 찍어 파일을 쉽게 가져올 수 있다.

 

 

😶 초기 세팅

 $ flutter pub add image_picker

터미널에 다음과 같이 명령어를 치거나 pubspec.yaml 에 image_picker: ^x.x.x 와 같이 최신 버전으로 직접 입력해 준다. 명명령어를 입력하면 자동으로 pubspec.yaml 파일에 추가해 준다. 그런 다음 pub get 버튼을 누른다.

 

 

😶 권한 추가

 

> IOS

다음과 같이 추가하라고 나와있으므로 info.plist 파일에 권한 정보를 추가한다.

 

$ios/Runner/info.plist

...
<key>NSPhotoLibraryUsageDescription</key>
<string>사진첩 권한을 허용해주세요.</string>
<key>NSCameraUsageDescription</key>
<string>카메라 권한을 허용해주세요.</string>
<key>NSMicrophoneUsageDescription</key>
<string>마이크 권한을 허용해주세요.</string>
...
</dict>
</plist>

 

 

 

😶 사용법

카메라 앱을 사용해 이미지나 비디오 파일을 가져오는 것은 로컬 캐시에 저장되므로 영구적으로 이용하고 싶다면 영구 저장소에 따로 저장하는 작업이 필요하다. 전체적인 사용법은 다음과 같다.

import 'package:image_picker/image_picker.dart';

final ImagePicker _picker = ImagePicker();

// Pick an image
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);

// Capture a photo
final XFile? photo = await _picker.pickImage(source: ImageSource.camera);
    
// Pick a video
final XFile? image = await _picker.pickVideo(source: ImageSource.gallery);
    
// Capture a video
final XFile? video = await _picker.pickVideo(source: ImageSource.camera);
    
// Pick multiple images
final List<XFile>? images = await _picker.pickMultiImage();

 

await 키워드가 필요한 것은 이미지나 비디오 파일을 가져오는 작업은 바로 실행되는 것이 아니라 사용자가 파일을 고를 때까지의 시간이 걸리기 때문에 비동기로 실행시키기 위한 것이다.

 

XFile 형식으로 파일 정보를 받으면 Flutter에서 사용할 땐 File 형식으로 바꿔줘야 한다. 그럴 때는 다음과 같이 바꾸면 된다.

XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery);
File file = File(image!.path);

// Image 위젯 예시
Image.asset(file);

 

 

예시) 갤러리에서 사진 가져오기

 

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

void main() => runApp(MaterialApp(home: MyApp()));

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp>  {
  XFile? _imageFile;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Container(
          padding: const EdgeInsets.all(20.0),
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
          child: Column(
            children: [
              ElevatedButton(
                onPressed: onGalleryBringPicture,
                child: const Text("갤러리에서 사진 가져오기"),
              ),
              Expanded(
                child: _imageFile != null
                    ? Image.file(File(_imageFile!.path))
                    : const Center(child: Text("사진이 없습니다.")),
              )
            ],
          ),
        ),
      ),
    );
  }

  void onGalleryBringPicture() async {
    XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery);
    
    if(image != null){
      setState(() {
        _imageFile = image;
      });
    }
  }
}

 

 

😶 안드로이드에서 주의해야 할 점

 

안드로이드에서 갤러리나 카메라 등에 접근해서 파일을 가져올 때 image_picker는 기본 Intent.ACTION_GET_CONTENT 또는  MediaStore.ACTION_IMAGE_CAPTURE을 사용한다. 이 intent 속성은 앱이 실행하는 동안 백그라운드로 이동하고  시스템 메모리가 부족할 시 정리될 수 있음을 뜻한다. 따라서 image_picker 가 파일을 가져오는 동안 시스템에 의해 정리되고 다시 실행될 때 원해 호출로 반환되지 않으므로 ImagePicker.retrieveLostData()를 활용해 데이터가 누락된 것이 있는지 확인하는 작업이 필요하다.

 

위의 갤러리에서 사진 가져오는 예시에서 retrieveLostData() 를 활용한 예시는 다음과 같다.

import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

void main() => runApp(MaterialApp(home: MyApp()));

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp>  {
  XFile? _imageFile;
  final ImagePicker _imagePicker = ImagePicker();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Container(
          padding: const EdgeInsets.all(20.0),
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
          child: Column(
            children: [
              ElevatedButton(
                onPressed: onGalleryBringPicture,
                child: const Text("갤러리에서 사진 가져오기"),
              ),
              Expanded(
                child: defaultTargetPlatform == TargetPlatform.android ? FutureBuilder<void>(
                  future: getLostData(),
                  builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
                    print("${snapshot.hasError} , ${snapshot.connectionState} , ${_imageFile}");
                    switch(snapshot.connectionState) {
                      case ConnectionState.none:
                      case ConnectionState.waiting:
                        return const Center(child: Text("사진이 없습니다."));
                      case ConnectionState.active:
                      case ConnectionState.done:
                        if(snapshot.hasError == false && _imageFile != null) {
                          return Image.file(File(_imageFile!.path));
                        } else {
                          return const Center(child: Text("사진이 없습니다."));
                        }
                    }
                  },
                ) : const Center(child: Text("사진이 없습니다.")),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> getLostData() async {
    final LostDataResponse response = await _imagePicker.retrieveLostData();
    if (response.isEmpty) {
      return;
    }
    if (response.files != null) {
      for (final XFile file in response.files!) {
        _imageFile = file;
        return;
      }
    } else {
      // error 발생 : response.exception
    }
  }

  void onGalleryBringPicture() async {
    XFile? image = await _imagePicker.pickImage(source: ImageSource.gallery);
    
    if(image != null){
      setState(() {
        _imageFile = image;
      });
    }
  }
}

 

728x90