[Flutter] 이미지 업로더 만들기 📷

dosilv·2021년 8월 18일
5
post-thumbnail

image picker를 이용해서 이미지 업로더를 만들어 보았다!

🧁 기본 세팅

먼저 이미지 피커를 다운받고 (flutter pub add image_picker)

ReadMe에 나와있는 대로 ios > Runner > Info.plist 파일에 아래 키들을 추가해 준다. (기기의 갤러리/카메라/마이크에 접근하기 위한 키!)

<key>NSPhotoLibraryUsageDescription</key>
<string></string>
<key>NSCameraUsageDescription</key>
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string></string>

string은 혹시 몰라서 비우더라도 넣었는데 안 써도 되는 듯...?!

그리고 위젯을 만들 다트 파일에 image picker와 dart:io를 임포트해 주자.

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


🧁 이미지 불러오기

우선 ImagePicker 객체와 선택된 이미지를 담을 리스트 변수를 선언해 준다.

그리고 이미지를 선택하도록 하는 함수를 작성한다. 비동기로!

  • ImagePicker의 pickMultiImage() 메서드로 이미지(XFile) 리스트를 받아오고, 다른 위젯들이 그걸 알 수 있도록 setState를 통해 _pickedImgs에 할당해 준다.

  • images != null 조건을 건 이유는, _pickedImgs는 non-nullable한 리스트로 선언했는데 null을 할당하면 안 되기 때문!

여기까지 하면 사용자가 선택한 이미지를 XFile 형태로 변수에 담을 수 있다. 그럼 이걸 바탕으로 업로더 레이아웃과 프리뷰를 만들어 보자~~~



🧁 레이아웃 잡기 & 버튼 만들기

GridView 위젯과 dotted_border를 이용했다.

👀 GridView

위젯들을 그리드 형태로 배치해 주는 위젯!
crossAxisCount로 가로로 배치할 위젯의 개수를 지정하고,
cross/mainAxisSpacing로 각각 가로/세로 아이템간 간격을 설정할 수 있다.

그런데 높이가 설정되지 않은 위젯 안에 GridView가 위치해 있을 경우
Vertical viewport was given unbounded height. 어쩌구 하는 에러가 뜬다...😕 SizedBoxContainer로 감싸고 height을 적당히 설정해 주면 해결됨~~

💡 [21.08.25 추가]
SizedBox, Container 대신 그냥 GridView 속성에 shrinkWrap: true를 추가해 줘도 됨!

여기까지하면 네모 상자 네 개가 생긴당.!

그 다음으로 컨테이너(위 코드에서 별표친 부분!) 안에 위젯을 넣어줄 건데, 1번째 박스에는 이미지 업로드 버튼을, 4번째 박스에는 업로드한 이미지가 4장을 초과할 때 개수를 표시할 카운터를 child로 전달한다.

인덱스가 필요해 List.generate로 그리드 아이템을 만들었기 때문에 똑같이 리스트 형태인 _boxContents를 선언했다. 인덱스 순서가 유지되어야 하기 때문에 아무 child가 필요없는 2, 3번째 요소로 빈 컨테이너를 할당했다.

4번째 박스에 들어갈 카운터도 이미지가 4장 초과로 업로드 되었을 때만 표시하면 되기 때문에 조건부로 _pinckedImgs.length <= 4 이면 빈 컨테이너를, 그 외의 경우에만 위젯을 넣어 줌!

그리고 아까 별표친 컨테이너 안에 아래처럼 전달해준다.



🧁 이미지 미리보기

그리고 이미지가 선택되었을 때 프리뷰 기능을 하도록 모든 박스에 BoxDecorationimage를 세팅해 준다.

  • 선택된 이미지의 개수가 1개이면 1번째 박스(index 0인 박스)에만, 2개이면 1, 2번째 박스(index 0, 1인 박스)에만 이미지가 들어가야 하기 때문에 index <= _pickedImgs.length - 1 이라는 조건을 걸어서 false일 경우 그냥 null이 되도록 함
  • 그리고 선택된 이미지가 정사각형이 아니더라도 각 칸에 빈 공간 없이 차도록 BoxFit.cover로 지정해쥼


🧁 이미지 서버로 전송하기

  1. 먼저 XFile 형태로 받은 이미지들을 MultipartFile 타입으로 변환해 준다.
final List<MultipartFile> _files = _pickedImgs.map((img) => MultipartFile.fromFileSync(img.path,  contentType: new MediaType("image", "jpg"))).toList();
  1. 백엔드에서 요구하는 키값으로 FormData 만들기!
FormData _formData = FormData.fromMap({"키값": _files});
  1. FormData는 http 말고 Dio로 전송한다!
Dio dio = Dio();

dio.options.headers["authorization"] = AuthProvider.token;
    dio.options.contentType = 'multipart/form-data';

final res = await dio.post(postNoteURL, data: _formData).then((res) {
  Get.back();
  return res.data;
});


🎉 결과물~~!


결과물인 척하는 몽실이 자랑 >,< 💙💙

profile
DevelOpErUN 성장일기🌈

5개의 댓글

comment-user-thumbnail
2021년 8월 20일

너무 멋졍....

1개의 답글
comment-user-thumbnail
2021년 12월 13일

안녕하세요, 올려주신 내용으로 열심히 공부하고 있는 flutter 초보입니다...
list _boxcontents ~~fontweight w800<--- 이 스크린샷 부분을 어디다 넣어야할지 몰라서 완전 헤매고 있습니다.. 혹시 전체 코드가 있으시면 공유 가능할까요 ㅠㅠㅠ 감사합니다.

1개의 답글
comment-user-thumbnail
2023년 8월 14일

안녕하세요.
http대신 Dio를 사용하신 이유가 궁금합니다..!

답글 달기