[flutter] screenshot 패키지 사용하기

KoEunseo·2023년 11월 16일
0

flutter

목록 보기
35/45

프로필 생성 부분을 개발하면서 스크린샷 패키지를 써야했다. 항상 새로운 패키지를 쓰는 것이 제일 어려운 것 같다. 막상 다 하고 보면 별거 아닌데.

그래서 내가 이 패키지를 쓴 방법에 대해 자세히 써보려고 한다.

처음 내 코드는 아래와 같다.

안된코드(컨트롤러)

     final capturedImage = await screenshotController.capture();

     if (capturedImage != null) {
    // 캡쳐는 제대로 되고있음.
     Get.dialog(Container(
       child: Image.memory(capturedImage),
     ));
    // 1. MultipartFile로 변환
     dioPackage.MultipartFile multipartFile =
         dioPackage.MultipartFile.fromBytes(
       capturedImage,
       filename: 'screenshot.png',
     );
    // 2. formdata를 생성
     dioPackage.FormData formData = dioPackage.FormData.fromMap({
       'file': multipartFile,
     });
     try {
       var res = await uploadController.uploadAsset(formData);
       print('성공: ${res.data}');
     } catch (e) {
       print(
           '업로드 실패: $e');
     }
     }
     print('캡쳐끝');
  }

이미지 캡쳐가 처음 되면 Uint8List 타입이 리턴된다.
이 캡쳐된 이미지를 MultipartFile로 변환한 후,
Formdata를 생성해 MultipartFile을 담아 'file'이라는 이름으로 전송했다.
이렇게 코드를 썼을 때 NoSuchMethodError: The getter 'data' was called on null. 이런 에러가 계속 나왔다. 근데 사실 이건 다른쪽 코드에서 나온 에러였다...(ㅠㅠ) 리턴되는 데이터 형식과 다른 형식으로 받아오려고 해서 나온 에러였음.

여튼간 최종적으로는 아래와 같이 이미지 upload관련 컨트롤러를 따로 빼고, 아바타를 캡쳐하고 프로필을 업로드하는 컨트롤러를 만드는 식으로 구현했다.

uploadController/uploadAsset func

이미지를 서버에 올리게 되면 url이 리턴된다. 그 url을 따로 RxString 값(imageUrl)으로 저장했다.

  uploadAsset(image) async {
    dio.options.contentType = 'multipart/form-data';
    String path = '/api/upload';
    try {
      var res = await dio.post(path, data: image);
      if (res.data["data"] != null) {
        imageUrl.value = res.data["data"][0]["url"];
      } else {
        Get.dialog(
          AvatarWithOneButton(
            mainMessage: '아바타를 수정하지 못했습니다.',
            subMessage: res.data['message'],
            buttonTitle: '확인하기',
          ),
        );
      }
    } catch (e) {
      print('uploadAsset: $e');
    }
  }

avatarController/captureAdnUpload func

1. 스크린샷으로 캡쳐하고

await screenshotController
        .capture(delay: const Duration(milliseconds: 10))
        .then((Uint8List? image) async {
      if (image != null) { ... }

2. then을 써서 바로 directory에 담아(getApplicationDocumentsDirectory)

final directory = await getApplicationDocumentsDirectory();

3. File로 만든다.

await File('${directory.path}/${DateTime.now()}.png').create()
이때 directory.path로 경로, DateTime.now로 현재 시각을 파일 이름으로 만든다.(파일이름이 매번 다를 수 있도록. 파일이름이 같으면 getX가 파일 내용물이 달라졌어도 감지하지 못한다.)

4. dio에서 제공하는 .MultipartFile.fromFile으로 파일을 multipartFile으로 변환한다. 이때 파일이름을 DateTime.now로 지정

await dioPackage.MultipartFile.fromFile(imagePath.path, filename: '${DateTime.now()}.png');

5. 역시 dio에서 제공하는 .FormData.fromMap을 이용해 서버에 전송 가능한 FormData 형식으로 변환한다.

dioPackage.FormData.fromMap({ 'file': multipartFile, });

6. 이제 uploadController에서 만들어둔 uploadAsset함수로 서버에 데이터를 전송하면 된다.

await uploadController.uploadAsset(formData);

captureAndUpload 전체 코드

void captureAndUpload() async {
    await screenshotController
        .capture(delay: const Duration(milliseconds: 10))
        .then((Uint8List? image) async {
      if (image != null) {
        final directory = await getApplicationDocumentsDirectory();
        final imagePath =
            await File('${directory.path}/${DateTime.now()}.png').create();
        await imagePath.writeAsBytes(image); //이부분은 삭제해도 무방;

        if (await imagePath.exists()) {
          final dioPackage.MultipartFile multipartFile =
              await dioPackage.MultipartFile.fromFile(imagePath.path,
                  filename: '${DateTime.now()}.png');

          dioPackage.FormData formData = dioPackage.FormData.fromMap({
            'file': multipartFile,
          });

          try {
            await uploadController.uploadAsset(formData);
            Get.dialog(
              AvatarWithOneButton(
                // avatar: imageUrl,
                uploadedAvatar: image != null ? MemoryImage(image) : null,
                mainMessage: '아바타 수정이 완료되었습니다.',
                buttonTitle: '확인하기',
              ),
            ).then((_) => Get.back());
          } catch (e) {
            print('capture and upload: $e');
          }
        }
      }
    });
  }
profile
주니어 플러터 개발자의 고군분투기

0개의 댓글