2023. 4. 12. 23:39ㆍFlutter
이미지 업로더에 이어 동영상 업로더도 만들어보자
이미지 업로더와 동일하게 image_picker도 사용하고, 미리 보기 출력을 위해 video_player도 pubspec.yaml에 추가해 준다
https://pub.dev/packages/image_picker
image_picker | Flutter Package
Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera.
pub.dev
https://pub.dev/packages/video_player
video_player | Flutter Package
Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web.
pub.dev
이미지 업로드때와 동일하게 동영상을 저장하기 위한 provider를 설정한다.
class SelectedVideoNotifier extends StateNotifier<List<XFile>> {
SelectedVideoNotifier() : super([]);
void insertVideo(XFile file) {
state = [file];
}
removeVideo() {
state = [];
}
}
final selectedVideoProvider = StateNotifierProvider<SelectedVideoNotifier, List<XFile>>((ref) {
return SelectedVideoNotifier();
});
final videoStateProvider = StateProvider((ref) => false);
insertVideo를 통해 동영상을 추가, removeVideo를 통해 동영상을 제거한다.
비디오 재생 상태 저장을 위한 videoStateProvider도 작성해 두었다.
동영상 선택과 ImagePicker와 동영상 재생을 위한 controller를 선언한다.
final ImagePicker _picker = ImagePicker();
late VideoPlayerController _controller;
비디오를 선택하는 함수를 작성하자
Future pickVideo(ImageSource source) async {
final XFile? file = await _picker.pickVideo(source: source, maxDuration: const Duration(seconds: 10));
if (file != null) {
ref.watch(selectedVideoProvider.notifier).insertVideo(file);
_controller = VideoPlayerController.network(file.path)
..initialize().then((_) {
_controller.addListener(() {
setState(() {
ref.watch(videoStateProvider.notifier).update((state) => _controller.value.isPlaying);
});
});
setState(() {});
});
}
}
동영상을 선택하면 insertVideo를 통해 동영상을 리스트에 담고 동영상의 path를 controller에 전달하여 동영상 재생이 가능하도록 만들어준다.
addListener로 비디오의 재생상태를 확인하며 값을 수정하도록 해주었다.
void dispose() {
super.dispose();
_controller.dispose();
}
dispose시 controller도 dispose 시켜준다.
동영상 업로드 및 미리 보기를 위한 간략한 위젯을 작성했다
@override
Widget build(BuildContext context) {
return Row(
children: [
InkWell(
onTap: () => pickVideo(ImageSource.gallery),
child: Icon(Icons.add_a_photo_outlined),
),
SizedBox(width: 5.w),
if (ref.watch(selectedVideoProvider).isNotEmpty && _controller.value.isInitialized)
Stack(
children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 2.w),
width: 80.w,
height: 80.w,
child: ClipRRect(
borderRadius: BorderRadius.circular(8.w),
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
),
),
),
Positioned(
right: 0,
child: InkWell(
onTap: () => ref.watch(selectedVideoProvider.notifier).removeVideo(),
child: Icon(Icons.close),
),
),
Positioned(
left: 0,
child: InkWell(
onTap: () => setState(() {
ref.watch(videoStateProvider) ? _controller.pause() : _controller.play();
}),
child: Icon(ref.watch(videoStateProvider) ? Icons.pause : Icons.play_arrow),
),
)
],
)
],
);
}
아이콘을 누르면 갤러리를 통해 동영상을 선택할 수 있다

x버튼을 누르면 동영상 리스트를 clear 시키며
재생 버튼을 누르면 버튼이 일시정지 모양으로 바뀌며 동영상이 재생되고, 재생이 끝나면 버튼이 다시 재생 모양으로 바뀌게 된다.

pick_video 전체코드
class PickVideo extends ConsumerStatefulWidget {
const PickVideo({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _PickVideoState();
}
class _PickVideoState extends ConsumerState<PickVideo> {
final ImagePicker _picker = ImagePicker();
late VideoPlayerController _controller;
@override
void dispose() {
super.dispose();
_controller.dispose();
}
Future pickVideo(ImageSource source) async {
final XFile? file = await _picker.pickVideo(source: source, maxDuration: const Duration(seconds: 10));
if (file != null) {
ref.watch(selectedVideoProvider.notifier).insertVideo(file);
_controller = VideoPlayerController.network(file.path)
..initialize().then((_) {
_controller.addListener(() {
setState(() {
ref.watch(videoStateProvider.notifier).update((state) => _controller.value.isPlaying);
});
});
setState(() {});
});
}
}
@override
Widget build(BuildContext context) {
return Row(
children: [
InkWell(
onTap: () => pickVideo(ImageSource.gallery),
child: Icon(Icons.add_a_photo_outlined),
),
SizedBox(width: 5.w),
if (ref.watch(selectedVideoProvider).isNotEmpty && _controller.value.isInitialized)
Stack(
children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 2.w),
width: 80.w,
height: 80.w,
child: ClipRRect(
borderRadius: BorderRadius.circular(8.w),
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
),
),
),
Positioned(
right: 0,
child: InkWell(
onTap: () => ref.watch(selectedVideoProvider.notifier).removeVideo(),
child: Icon(Icons.close),
),
),
Positioned(
left: 0,
child: InkWell(
onTap: () => setState(() {
ref.watch(videoStateProvider) ? _controller.pause() : _controller.play();
}),
child: Icon(ref.watch(videoStateProvider) ? Icons.pause : Icons.play_arrow),
),
)
],
)
],
);
}
}
'Flutter' 카테고리의 다른 글
addPostFrameCallback 사용하기 (0) | 2023.12.13 |
---|---|
[Riverpod] ref가 없는 곳에서 ref 쓰기? (0) | 2023.12.08 |
플러터로 이미지 업로더 만들기 (with Riverpod) (0) | 2023.04.12 |
[Riverpod] #3 Provider의 사용 방법 (0) | 2023.04.12 |
[Riverpod] #2 Provider의 종류 (0) | 2023.04.04 |