스마트시대
22 RIVERPOD 22.1 NotifierProvider 22.2 ConsumerWidget 22.3 recap for ConsumerWidget 22.4 AsyncNotifierProvider 본문
Programing/Flutter
22 RIVERPOD 22.1 NotifierProvider 22.2 ConsumerWidget 22.3 recap for ConsumerWidget 22.4 AsyncNotifierProvider
스마트시대 2023. 5. 28. 22:07728x90

22.1 NotifierProvider



import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tiktok_clone/features/videos/models/playback_config_model.dart';
import 'package:tiktok_clone/features/videos/repos/playback_config_repo.dart';
//(21.3)
// class PlaybackConfigViewModel extends ChangeNotifier {
//(22.1)
class PlaybackConfigViewModel extends Notifier<PlaybackConfigModel> {
final PlaybackConfigRepository _repository;
// late final PlaybackConfigModel _model = PlaybackConfigModel(
// muted: _repository.isMuted(),
// autoplay: _repository.isAutoplay(),
// );
PlaybackConfigViewModel(this._repository);
// bool get muted => _model.muted;
// bool get autoplay => _model.autoplay;
void setMuted(bool value) {
_repository.setMuted(value); // repository에서 값을 디스크에 persist(지속)하게 저장
// _model.muted = value; //model 수정
// notifyListeners(); //listen하고 있는 모두에게 notify해줌(21.3)
//(22.1)riverpod에서는 _model을 mutated(바꿔오다)할 수없고 완전 새로운 state를 만들어야함(refactor).
state = PlaybackConfigModel(
muted: value,
autoplay: state.autoplay,
);
}
void setAutoplay(bool value) {
_repository.setAutoplay(value);
// _model.autoplay = value;
// notifyListeners();(21.3)
//(22.1)riverpod에서는 _model을 mutated(바꿔오다)할 수없고 완전 새로운 state를 만들어야함(refactor).
state = PlaybackConfigModel(
muted: state.muted,
autoplay: value,
);
}
//(22.1)initialized data
@override
PlaybackConfigModel build() {
return PlaybackConfigModel(
muted: _repository.isMuted(),
autoplay: _repository.isAutoplay(),
);
}
}
final playbackConfigProvider =
NotifierProvider<PlaybackConfigViewModel, PlaybackConfigModel>(
//(22.1)여기서만 하니까 일단 무시
() => throw UnimplementedError(),
);
22.2 ConsumerWidget

// class SettingsScreen extends StatefulWidget {
-------------------
//(22.2에서 stateless위젯으로 바꾸고 ConsumerWidget로 다시 바꾸기)
class SettingsScreen extends ConsumerWidget {
-------------------
const SettingsScreen({super.key});
@override
-------------------
//(22.2)
Widget build(BuildContext context, WidgetRef ref) {
-------------------
// 언어 재설정 안해도 되는 위젯
return Localizations.override(
context: context,
SwitchListTile.adaptive(
-------------------
//(22.2)
value: ref.watch(playbackConfigProvider).muted,
onChanged: (value) =>
ref.read(playbackConfigProvider.notifier).setMuted(value),
-------------------
title: const Text("Auto Mute video"),
subtitle: const Text("Videos will be muted by default."),
),
SwitchListTile.adaptive(
-------------------
//(22.2)
value: ref.watch(playbackConfigProvider).autoplay,
onChanged: (value) =>
ref.read(playbackConfigProvider.notifier).setAutoplay(value),
-------------------
title: const Text("Autoplay"),
subtitle: const Text("Videos will start playing automatically."),
),
Checkbox(
-------------------
//(22.2)
// value: _notifications,
// onChanged: _onNotificationsChaged,
-------------------
value: false,
onChanged: (value) {},
),
CheckboxListTile(
activeColor: Colors.black,
-------------------
//(22.2)
// value: _notifications,
// onChanged: _onNotificationsChaged,
-------------------
value: false,
onChanged: (value) {},
title: const Text("Enable notifications"),
),
ListTile(
onTap: () async {
final date = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1980),
lastDate: DateTime(2030),
);
if (kDebugMode) {
print(date);
}
-------------------
//(22.2)
// if (!mounted) return;
-------------------
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (kDebugMode) {
print(time);
}
-------------------
//(22.2)
// if (!mounted) return;
-------------------
final booking = await showDateRangePicker(
context: context,
firstDate: DateTime(1980),
lastDate: DateTime(2030),
builder: (context, child) {
return Theme(
data: ThemeData(
appBarTheme: const AppBarTheme(
foregroundColor: Colors.white,
backgroundColor: Colors.black,
밑에 두 버튼 누르면 비디오가 재생되지 않는 슬라이드가 넘어가는 걸 볼 수 있다.



22.3 recap for ConsumerWidget

-----------------
//(22.3)
class VideoPost extends ConsumerStatefulWidget {
-----------------
//onVideoFinished 처리해주기 위한 property,statefulWideget은 여기 있는데 작업은 state에서 해줄 필요
final Function onVideoFinished;
required this.index,
});
@override
// State<VideoPost> createState() => _VideoPostState();
-----------------
//(22.3)
VideoPostState createState() => VideoPostState();
-----------------
}
-----------------
//(22.3)
class VideoPostState extends ConsumerState<VideoPost>
-----------------
with SingleTickerProviderStateMixin {
//VideoPlayerController이용하기 위한 함수
late final VideoPlayerController _videoPlayerController;
//animation을 커스터마이즈하는 함수
final Duration _animationDuration = const Duration(milliseconds: 200);
late final AnimationController _animationController;
-----------------
//(22.3)
bool _isPaused = false;
final bool _isMuted = false;
-----------------
super.dispose();
}
//(21.4)볼륨 음소거 메소드(진짜)
void _onPlaybackConfigChanged() {
if (!mounted) return;
-----------------
//(22.3)
// final muted = context.read<PlaybackConfigViewModel>().muted;
final muted = ref.read(playbackConfigProvider).muted;
-----------------
-----------------
//(22.3)
// if (false) {
ref.read(playbackConfigProvider.notifier).setMuted(!muted);
if (muted) {
_videoPlayerController.setVolume(0);
} else {
_videoPlayerController.setVolume(1);
}
}
-----------------
// 비디오가 보여지는 영역을 계산하고 한쪽에서만 영상 재생되도록 해주는 파라메터
void _onVisibilityChanged(VisibilityInfo info) {
// mounted property: 위젯이 마운트되었는지 아닌지 알려줌
if (!mounted) return;
if (info.visibleFraction == 1 &&
!_isPaused &&
!_videoPlayerController.value.isPlaying) {
//(21.4)볼륨 음소거 (진짜)
// final autoplay = context.read<PlaybackConfigViewModel>().autoplay;
-----------------
//(22.3)
// if (false) {
if (ref.read(playbackConfigProvider).autoplay) {
_videoPlayerController.play();
}
}
-----------------
//영상이 재생되고 있는 상태에서 다른 탭으로 넘어가면 리소스 폐기(dispose, 영상정지)해주는 설정
if (_videoPlayerController.value.isPlaying && info.visibleFraction == 0) {
_onTogglePause();
}
child: IconButton(
icon: FaIcon(
---------------------
//(22.3)
ref.watch(playbackConfigProvider).muted
---------------------
? FontAwesomeIcons.volumeOff
: FontAwesomeIcons.volumeHigh,
color: Colors.white,
),
onPressed:
--------------------- ---------------------
//(22.3)
_onPlaybackConfigChanged,
--------------------- ---------------------
),
),
Positioned(
22.4 AsyncNotifierProvider
이 viewModel은 API로부터 얻어 온 값을 제공한다.
// 이 build메소드에서 우리가 원하는 API를 호출하고
// 데이터를 반환하면 그 데이터는
//우리의 Provider에 의해 expose됨(그 데이터는 VideoModel의 List 형태여야함)

//(22.4)
// class VideoTimelineScreen extends StatefulWidget {
class VideoTimelineScreen extends ConsumerStatefulWidget {
const VideoTimelineScreen({super.key});
@override
//(22.4)
VideoTimelineScreenState createState() => VideoTimelineScreenState();
}
//(22.4)
class VideoTimelineScreenState extends ConsumerState<VideoTimelineScreen> {
int _itemCount = 4;
----------------
//스크린 넘어갈 때 느려지는 효과 없애는 함수:Pageview.builder를 컨트롤 하는 파라미터
final PageController _pageController = PageController();
const Duration(seconds: 5),
);
}
@override
Widget build(BuildContext context) {
----------------
//(22.4)
return ref.watch(timelineProvider).when(
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (error, StackTrace) => Center(
child: Text(
"Could not load videos $error",
style: const TextStyle(
color: Colors.white,
),
),
),
data: (videos) => RefreshIndicator(
onRefresh: _onRefresh,
displacement: 50,
edgeOffset: 20,
color: Theme.of(context).primaryColor,
child: PageView.builder(
controller: _pageController,
scrollDirection: Axis.vertical,
onPageChanged: _onPageChanged,
----------------
//(22.4)
itemCount: videos.length,
----------------
itemBuilder: (context, index) =>
VideoPost(onVideoFinished: _onVideoFinished, index: index),
),
),
);
----------------

import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tiktok_clone/features/videos/models/video_model.dart';
//(22.4)
//우리의 Provider에 의해 expose됨(그 데이터는 VideoModel의 List 형태여야함)
class TimelineViewModel extends AsyncNotifier<List<VideoModel>> {
List<VideoModel> _list = [];
void uploadVideo() async {
//loading state를 다시 트리거시키는 방법
state = const AsyncValue.loading();
await Future.delayed(
const Duration(seconds: 2),
);
final newVideo = VideoModel(title: "${DateTime.now()}");
_list = [..._list, newVideo];
//새로운 데이더 받아오려면 이런식으로
state = AsyncValue.data(_list);
}
@override
FutureOr<List<VideoModel>> build() async {
// 이 build메소드에서 우리가 원하는 API를 호출하고
await Future.delayed(
const Duration(seconds: 5),
);
// 데이터를 반환하면 그 데이터는
return _list;
}
}
final timelineProvider =
AsyncNotifierProvider<TimelineViewModel, List<VideoModel>>(
() => TimelineViewModel(),
);

//(22.4)
class VideoPreviewScreen extends ConsumerStatefulWidget {
final XFile video;
//동영상 저장 아이콘은 사진찍고 확인할 스크린에서만 표시
final bool isPicked;
const VideoPreviewScreen({
super.key,
required this.video,
required this.isPicked,
});
@override
//(22.4)
VideoPreviewScreenState createState() => VideoPreviewScreenState();
}
//(22.4)
class VideoPreviewScreenState extends ConsumerState<VideoPreviewScreen> {
late final VideoPlayerController _videoPlayerController;
---------------------
_savedVideo = true;
setState(() {});
}
---------------------
//(22.4)
void _onUploadPressed() {
ref.read(timelineProvider.notifier).uploadVideo();
}
---------------------
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: const Text("preview video"),
actions: [
//동영상 저장 아이콘은 사진찍고 확인할 스크린에서만 표시
if (!widget.isPicked)
IconButton(
onPressed: _saveToGallery,
icon: FaIcon(
_savedVideo
? FontAwesomeIcons.check
: FontAwesomeIcons.download,
),
),
---------------------
//(22.4)
IconButton(
onPressed: ref.watch(timelineProvider).isLoading
? () {}
: _onUploadPressed,
icon: ref.watch(timelineProvider).isLoading
? const CircularProgressIndicator()
: const FaIcon(
FontAwesomeIcons.cloudArrowUp,
),
---------------------
),
],
),
body: _videoPlayerController.value.isInitialized


728x90
반응형
'Programing > Flutter' 카테고리의 다른 글
Comments