simple_asset_picker
카카오톡의 UI를 참고하여 만든 플러터 이미지피커 패키지입니다. 안드로이드와 ios만 지원하고 있으며 photo_manager와 flutter_riverpod, video_player 패키지를 이용했습니다. 사진 및 영상 선택, 미리보기 기능을 지원하고 있습니다. 다음 버전에서 카메라로 사진을 찍은후 선택하는 기능을 추가할 예정입니다.
스크린샷
사용방법
dependency 추가
dependencies:
simple_asset_picker: 최신버전 사용
안드로이드
android/app/src/main/AndroidManifest.xml 파일에 권한 추가
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.INTERNET"/>
android/app/build.gradle에 minSdk 수정
defaultConfig {
...
minSdkVersion 21
...
}
ios
ios/Runner/Info.plist에 권한 설정 추가
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>갤러리 접근 권한이 필요합니다.</string>
<key>NSCameraUsageDescription</key>
<string>카메라 접근 권한이 필요합니다.</string>
<key>NSMicrophoneUsageDescription</key>
<string>영상 촬영을 위한 마이크 권한이 필요합니다.</string>
공통
권한설정 거부 시 '권한이 없습니다.' 메세지만 출력하고 있으니 권한이 꼭 필요한 경우
PhotoManager.openSetting();
openAppSetting();
을 통해 앱설정에서 허용하도록 따로 처리해야합니다.
main 설정
main의 runApp에서 ProviderScope로 감싸준다(필수)
void main() {
runApp(
const ProviderScope(child: MyApp()),
);
}
사진 선택창 띄우기
Picker.pickAssets으로 사진 선택 기능 사용
PickerConfig를 통해 옵션 설정 가능
final List<AssetEntity> list =
await Picker.pickAssets(
context,
pickerConfig: PickerConfig(
// 선택한 사진 테두리 색상
mainColor: Colors.green,
// 선택사진 갯수 텍스트 색상
brightness: Brightness.light,
// 사진 목록 한줄에 표시할 갯수
gridCount: 3,
// 최대 선택가능 갯수
maxAssets: 10,
// 사진 목록 페이징 사이즈
pageSize: 30,
// asset타입(사진, 영상, 음성)
requestType: RequestType.common,
// 기존에 선택한 사진
selectedAssets: selectedlist,
// 다음 버전에서 구현예정
// 현버전에서는 false 권장
useCamera: false,
),
);
선택한 사진, 영상 표시
photo_manager 패키지를 이용했기 때문에 해당 패키지에서 제공하는 방법을 사용하면 됩니다.
영상은 썸네일 이미지만 표시됩니다.
AssetEntityImage(asset, isOriginal: false);
또는
Image(image: AssetEntityImageProvider(asset, isOriginal: false));
multipart/form-data 전송
해당 내용도 photo_manager에서 제공하는 방법을 그대로 적어둔 것입니다.
http 사용
pseudo code :
import 'package:http/http.dart' as http;
Future<void> upload() async {
final entity = await obtainYourEntity();
final uri = Uri.https('example.com', 'create');
final request = http.MultipartRequest('POST', uri)
..fields['test_field'] = 'test_value'
..files.add(await multipartFileFromAssetEntity(entity));
final response = await request.send();
if (response.statusCode == 200) {
print('Uploaded!');
}
}
Future<http.MultipartFile> multipartFileFromAssetEntity(AssetEntity entity) async {
http.MultipartFile mf;
// Using the file path.
final file = await entity.file;
if (file == null) {
throw StateError('Unable to obtain file of the entity ${entity.id}.');
}
mf = await http.MultipartFile.fromPath('test_file', file.path);
// Using the bytes.
final bytes = await entity.originBytes;
if (bytes == null) {
throw StateError('Unable to obtain bytes of the entity ${entity.id}.');
}
mf = http.MultipartFile.fromBytes('test_file', bytes);
return mf;
}
dio 사용
pseudo code :
import 'package:dio/dio.dart' as dio;
Future<void> upload() async {
final entity = await obtainYourEntity();
final uri = Uri.https('example.com', 'create');
final response = dio.Dio().requestUri(
uri,
data: dio.FormData.fromMap({
'test_field': 'test_value',
'test_file': await multipartFileFromAssetEntity(entity),
}),
);
print('Uploaded!');
}
Future<dio.MultipartFile> multipartFileFromAssetEntity(AssetEntity entity) async {
dio.MultipartFile mf;
// Using the file path.
final file = await entity.file;
if (file == null) {
throw StateError('Unable to obtain file of the entity ${entity.id}.');
}
mf = await dio.MultipartFile.fromFile(file.path);
// Using the bytes.
final bytes = await entity.originBytes;
if (bytes == null) {
throw StateError('Unable to obtain bytes of the entity ${entity.id}.');
}
mf = dio.MultipartFile.fromBytes(bytes);
return mf;
}