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를 이용했다.
위젯들을 그리드 형태로 배치해 주는 위젯!
crossAxisCount로 가로로 배치할 위젯의 개수를 지정하고,
cross/mainAxisSpacing로 각각 가로/세로 아이템간 간격을 설정할 수 있다.
그런데 높이가 설정되지 않은 위젯 안에 GridView가 위치해 있을 경우
Vertical viewport was given unbounded height.
어쩌구 하는 에러가 뜬다...😕 SizedBox나 Container로 감싸고 height을 적당히 설정해 주면 해결됨~~
💡 [21.08.25 추가]
SizedBox, Container 대신 그냥 GridView 속성에 shrinkWrap: true를 추가해 줘도 됨!
여기까지하면 네모 상자 네 개가 생긴당.!
그 다음으로 컨테이너(위 코드에서 별표친 부분!) 안에 위젯을 넣어줄 건데, 1번째 박스에는 이미지 업로드 버튼을, 4번째 박스에는 업로드한 이미지가 4장을 초과할 때 개수를 표시할 카운터를 child로 전달한다.
인덱스가 필요해 List.generate로 그리드 아이템을 만들었기 때문에 똑같이 리스트 형태인 _boxContents를 선언했다. 인덱스 순서가 유지되어야 하기 때문에 아무 child가 필요없는 2, 3번째 요소로 빈 컨테이너를 할당했다.
4번째 박스에 들어갈 카운터도 이미지가 4장 초과로 업로드 되었을 때만 표시하면 되기 때문에 조건부로 _pinckedImgs.length <= 4
이면 빈 컨테이너를, 그 외의 경우에만 위젯을 넣어 줌!
그리고 아까 별표친 컨테이너 안에 아래처럼 전달해준다.
그리고 이미지가 선택되었을 때 프리뷰 기능을 하도록 모든 박스에 BoxDecoration의 image를 세팅해 준다.
index <= _pickedImgs.length - 1
이라는 조건을 걸어서 false일 경우 그냥 null이 되도록 함final List<MultipartFile> _files = _pickedImgs.map((img) => MultipartFile.fromFileSync(img.path, contentType: new MediaType("image", "jpg"))).toList();
FormData _formData = FormData.fromMap({"키값": _files});
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;
});
결과물인 척하는 몽실이 자랑 >,< 💙💙
너무 멋졍....