이 코드는 Flutter에서 Riverpod를 사용하여 비디오 타임라인과 비디오 업로드 기능을 구현하는 방법을 보여줍니다. AsyncNotifier
를 활용해 비동기적으로 비디오 목록을 관리하고, ConsumerStatefulWidget
을 사용해 상태 변화에 따라 UI를 업데이트합니다.
VideoModel
: 비디오의 데이터 모델입니다. 여기서는 간단하게 제목만 포함하고 있습니다.TimelineViewModel
: 비디오 타임라인의 뷰 모델로, AsyncNotifier
를 상속받아 비동기 로직을 처리합니다. 비디오 업로드를 시뮬레이션하는 uploadVideo
메서드를 포함하고 있습니다.timelineProvider
: TimelineViewModel
의 인스턴스를 제공하는 프로바이더입니다.VideoPreviewScreen
(비디오 미리보기 화면):ConsumerStatefulWidget
을 사용해 Riverpod의 상태 관리 기능을 활용합니다. 사용자가 비디오를 업로드하려 할 때 timelineProvider
의 uploadVideo
메서드를 호출합니다.VideoTimelineScreen
(비디오 타임라인 화면):ConsumerStatefulWidget
을 사용하며, timelineProvider
에서 제공하는 상태에 따라 다른 UI를 표시합니다. 상태가 로딩 중일 때는 로딩 인디케이터를, 에러가 발생했을 때는 에러 메시지를, 데이터가 준비되었을 때는 RefreshIndicator
와 함께 PageView.builder
를 사용해 비디오 목록을 표시합니다.class VideoModel {
String title;
VideoModel({required this.title});
}
class TimelineViewModel extends AsyncNotifier<List<VideoModel>> {
List<VideoModel> _list = [];
Future<void> uploadVideo() async {
state = const AsyncValue.loading(); // 비디오 업로드 시작 시 로딩 상태로 설정
await Future.delayed(const Duration(seconds: 2)); // 비디오 업로드를 시뮬레이션하는 지연
final newVideo = VideoModel(title: "${DateTime.now()}"); // 새 비디오 생성
_list = [..._list, newVideo]; // 비디오 목록에 새 비디오 추가
state = AsyncValue.data(_list); // 상태를 업데이트하여 UI에 반영
}
}
class VideoPreviewScreen extends ConsumerStatefulWidget {
final XFile video;
final bool isPicked;
const VideoPreviewScreen({super.key, required this.video, this.isPicked = false});
VideoPreviewScreenState createState() => VideoPreviewScreenState();
}
class VideoPreviewScreenState extends ConsumerState<VideoPreviewScreen> {
late final VideoPlayerController _videoPlayerController;
bool _savedVideo = false;
void initState() {
super.initState();
_initializeVideoPlayer(); // 비디오 플레이어 초기화
}
void _onUploadPressed() async {
ref.read(timelineProvider.notifier).uploadVideo(); // 업로드 버튼 클릭 시 비디오 업로드 메서드 호출
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('설정'),
actions: [
IconButton(
onPressed: ref.watch(timelineProvider).isLoading
? null // 로딩 중인 경우 업로드 버튼 비활성화
: _onUploadPressed, // 로딩이 아닌 경우 업로드 메서드 호출
icon: ref.watch(timelineProvider).isLoading
? const CircularProgressIndicator() // 로딩 중인 경우 로딩 인디케이터 표시
: const FaIcon(FontAwesomeIcons.cloudArrowUp), // 로딩이 아닌 경우 업로드 아이콘 표시
)
],
),
body: // UI 구성 생략
);
}
}
이 코드는 비디오 앱의 기본적인 기능을 구현하는 데 필요한 Riverpod 사용법을 보여줍니다. AsyncNotifier
와 ConsumerStatefulWidget
을 사용하여 비동기 작업의 상태 관리와 UI 반영을 보다 간결하고 효율적으로 처리할 수 있습니다.
아래는 VideoTimelineScreen
의 build
메서드에서 ref.watch(timelineProvider).when(...)
을 사용하여 비디오 타임라인을 동적으로 렌더링하는 방법에 대한 코드 예시와 함께 한국어로 된 주석을 포함한 설명입니다.
// VideoTimelineScreen의 build 메서드 내부
Widget build(BuildContext context) {
// timelineProvider를 구독하고 현재 상태에 따라 조건부로 다른 위젯을 렌더링합니다.
return ref.watch(timelineProvider).when(
// 로딩 중 상태: CircularProgressIndicator를 중앙에 표시합니다.
loading: () => const Center(
child: CircularProgressIndicator(),
),
// 에러 상태: 에러 메시지를 표시합니다.
error: (error, stackTrace) => Center(
child: Text(
'비디오를 불러올 수 없습니다: $error',
style: const TextStyle(color: Colors.white),
),
),
// 데이터 로딩 완료 상태: RefreshIndicator와 PageView.builder를 사용하여 비디오 목록을 표시합니다.
data: (videos) => RefreshIndicator(
onRefresh: _onRefresh, // 화면을 아래로 당겨서 새로고침할 때 호출될 메서드
displacement: 50, // 새로고침 인디케이터의 위치 조정
edgeOffset: 20, // 새로고침 인디케이터와 화면 상단 간의 간격
color: Theme.of(context).primaryColor, // 인디케이터의 색상
child: PageView.builder(
controller: _pageController, // 페이지를 컨트롤할 PageController
scrollDirection: Axis.vertical, // 스크롤 방향을 수직으로 설정
onPageChanged: _onPageChanged, // 페이지 변경 시 호출될 메서드
itemCount: videos.length, // 비디오 목록의 길이에 따라 페이지 수 결정
itemBuilder: (context, index) => VideoPost( // 각 비디오 항목을 렌더링
onVideoFinished: _onVideoFinished, // 비디오 재생 완료 시 호출될 메서드
index: index, // 현재 비디오의 인덱스
),
),
),
);
}
이 코드에서는 timelineProvider
의 상태를 ref.watch
를 통해 구독하고 있으며, .when
메서드를 사용해 해당 상태가 loading
, error
, data
중 어느 것인지에 따라 다른 위젯을 렌더링합니다. 이러한 방식으로 비동기 데이터 로딩 상태를 관리하고 사용자에게 적절한 피드백(로딩 인디케이터, 에러 메시지, 비디오 목록)을 제공할 수 있습니다. 이는 비동기 작업의 결과를 처리하고 UI를 업데이트하는 효율적인 방법을 제공합니다.