이미지 업로드
다음과 같은 기능을 포함해 구현했다.
1. 초기 설정
permission_handler (접근 권한 허용 여부 확인), image_picker (이미지 선택), dio (백과의 통신) 패키지를 설치하고, android/app/src/main/AndroidManifest.xml에 아래 코드를 추가해준다.
// pubspec.yaml
dependencies:
...
permission_handler: ^10.3.0
image_picker: ^0.8.6+2
dio: ^4.0.6
// android/app/src/main/AndroidManifest.xml
<manifest ...>
// 저장공간 접근
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
...
2. 갤러리/파일 접근 권한이 허용되었을 경우, 갤러리/파일로 이동하고, 허용되지 않았을 경우, 모달 띄우기
갤러리/파일 접근 권한을 확인하고, 허용 여부에 따라 다르게 처리한다. 접근 권한이 없을 경우, 모달을 띄워주고, 있을 경우 휴대폰 내의 저장 공간으로 이동해 이미지를 선택해주었다.
// stateful widget (앞 부분은 생략)
XFile? selectedImage;
void imagePicker() async {
Permission.storage.request().then((value) {
if (value == PermissionStatus.denied ||
value == PermissionStatus.permanentlyDenied) {
// 접근 권한이 없음을 알리는 모달 띄우기
} else {
final ImagePicker picker = ImagePicker();
picker.pickImage(
source: ImageSource.gallery,
imageQuality: 50,
)
.then((localImage) {
setState(() {
selectedImage = localImage;
isImageUpdated = true;
});
}).catchError((error) {
// 오류 발생했음을 알리는 모달 띄우기
});
}
});
}
3. 선택된 이미지를 화면에 띄우기
이미지가 선택됐을 경우와 선택되지 않았을 경우를 나눠서, 선택됐을 경우, 화면에 이미지를 띄워줬다.
// 상자
class CustomBoxContainer extends StatelessWidget {
final bool hasRoundEdge, center;
final Color? borderColor;
final Color color;
final BoxShadowList? boxShadow;
final double? width, height;
final Widget? child;
final DecorationImage? image;
final ReturnVoid? onTap, onLongPress;
final BorderRadiusGeometry? borderRadius;
const CustomBoxContainer({
super.key,
this.hasRoundEdge = true,
this.borderColor,
this.color = whiteColor,
this.boxShadow,
this.width,
this.height,
this.image,
this.child,
this.onTap,
this.onLongPress,
this.center = false,
this.borderRadius,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: onLongPress,
onTap: onTap,
child: Container(
width: width,
height: height,
alignment: center ? Alignment.center : null,
decoration: BoxDecoration(
borderRadius:
borderRadius ?? (hasRoundEdge ? BorderRadius.circular(10) : null),
color: color,
boxShadow: boxShadow,
border: borderColor != null ? Border.all(color: borderColor!) : null,
image: image,
),
child: child,
),
);
}
}
// 아이콘 버튼
class CustomIconButton extends StatelessWidget {
final IconData icon;
final ReturnVoid onPressed;
final Color color;
final double size;
const CustomIconButton(
{super.key,
required this.onPressed,
required this.icon,
this.size = 30,
this.color = blackColor});
@override
Widget build(BuildContext context) {
return IconButton(
onPressed: onPressed,
icon: CustomIcon(
icon: icon,
color: color,
size: size,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
iconSize: size,
);
}
}
// 생략
if (selectedImage == null) {
return CustomBoxContainer(
width: 270,
height: 150,
onTap: imagePicker,
borderColor: greyColor,
hasRoundEdge: false,
child: CustomIconButton(
icon: addIcon,
onPressed: imagePicker,
color: greyColor,
),
);
} else {
return CustomBoxContainer(
width: 270,
height: 150,
onTap: imagePicker,
image: DecorationImage(
fit: BoxFit.fill,
image: FileImage(File(selectedImage!.path)),
),
// X 버튼 터치 시 이미지 삭제
child: CustomIconButton(
onPressed: deleteImage,
icon: closeIcon,
),
);
}
4. 이미지를 form data를 이용해 보내기
추가로 전달할 map 형태의 데이터가 있다면 jsonEncode 함수를 이용해 json으로 변환해보내주면 된다. 이미지 파일이 존재할 경우에만 MultipartFile.fromFileSync를 통해 변환해주었다.
// 토큰 생략
final data = {}; // 추가로 전달할 데이터
final baseUrl = 'baseUrl 넣어주기';
final dio = Dio(BaseOptions(baseUrl: baseUrl, contentType: 'multipart/form-data'));
dio.post('path 넣어주기', data: FormData.fromMap({
'data': jsonEncode(data), // map을 json 형태로 변환
'img': selectedImage != null
? MultipartFile.fromFileSync(
selectedImage!.path,
contentType: MediaType('image', 'png'),
)
: null,
'update_img': isImageUpdated,
}))).then((response) {
// 성공했을 때
}).catchError((error) {
// 실패했을 때
})
'앱 > flutter' 카테고리의 다른 글
[flutter] 하단 바에 그라데이션 적용하기 (snake navigation bar) (0) | 2023.08.21 |
---|---|
[flutter/kakao login] invalid key hash 오류 (0) | 2023.08.07 |
[flutter/kakao login/android] 플러터 안드로이드 앱에 카카오 로그인 적용하기 (0) | 2023.08.07 |
[flutter] [android studio] [Mac] Unable to find bundled Java version 오류 해결 (0) | 2023.01.31 |