스마트시대
VIDEO TIMELINE 7.5 AnimationController 7.6 AnimatedBuilder 7.7 SingleTickerProviderStateMixin 7.8 Video UI 7.9 RefreshIndicator 본문
VIDEO TIMELINE 7.5 AnimationController 7.6 AnimatedBuilder 7.7 SingleTickerProviderStateMixin 7.8 Video UI 7.9 RefreshIndicator
스마트시대 2023. 5. 16. 10:507.5 AnimationController
animation을 커스터마이즈하는 요소 아래와 같이 써주기
1. 이벤트 리스너를 사용하는 방법
VideoPlayerController.asset("assets/videos/video.mp4");
-----------
//animation을 커스터마이즈하는 함수
final Duration _animationDuration = const Duration(milliseconds: 200);
late final AnimationController _animationController;
bool _isPaused = false;
-----------
//VideoPlayerController이용하기 위한 함수
void _initVideoPlayer() async {
await _videoPlayerController.initialize();
//autoplay
// _videoPlayerController.play();
-----------
//VideoPlayerController를 어떻게 처리해줄 지 지정하는 listener
_videoPlayerController.addListener(_onVideoChange);
setState(() {});
}
-----------
_initVideoPlayer();
-----------
//animation을 커스터마이즈하는 parameter
_animationController = AnimationController(
vsync: this,
lowerBound: 1.0,
upperBound: 1.5,
value: 1.5, //디폴트 표시
duration: _animationDuration,
);
// 위의 중간값을 애니메이션 처리해주는(밑에 build 매소드가 중간값 읽기위한) 이벤트 리스너1
_animationController.addListener(() {
setState(() {});
});
}
-----------
//토글하면 화면 멈추기 파라메터
void _onTogglePause() {
if (_videoPlayerController.value.isPlaying) {
_videoPlayerController.pause();
-----------
//정지 누르면 재생아이콘 lowerBound: 1.0된다.
_animationController.reverse();
-----------
} else {
_videoPlayerController.play();
-----------
//플레이 누르면 재생아이콘 upperBound: 1.5,된다.
_animationController.forward();
}
setState(() {
_isPaused = !_isPaused;
});
}
-----------
onTap: _onTogglePause,
),
),
-----------
Positioned.fill(
//아이콘을 GestureDetector로 또 감싸는게 아닌 IgnorePointer로 _onTogglePause 이벤트리스너 듣게 하기
child: IgnorePointer(
child: Center(
//animation을 커스터마이즈하는 parameter 1
child: Transform.scale(
scale: _animationController.value,
-----------
7.6 AnimatedBuilder
2. 위젯을 사용하는 방법
animation: _animationController,
--------------
//builder라는 함수는 animationController의 값이 변할 때마다 실행됨
//그럴 때마다 AnimatedBuilder는 밑의 메서드를 실행한다.
builder: (context, child) {
return Transform.scale(
scale: _animationController.value,
child: child, //여기서의 : child는 AnimatedOpacity
);
},
--------------
7.7 SingleTickerProviderStateMixin
class _VideoPostState extends State<VideoPost> with SingleTickerProviderStateMixin {
//animation을 커스터마이즈하는 하기 위해 클래스에 with이하 추가 //마이크로초보다 더빠른 시계로(ticker가 tick하는)매 프레임마다 callback호출함->그니까vsync로 제약둘 필요
//animation을 커스터마이즈하는 parameter
_animationController = AnimationController(
//offscreen의 불필요한 리소스 사용막기:위젯이 안보일때는 애니 작동안함
//this:_VideoPostState class
vsync: this,
lowerBound: 1.0,
upperBound: 1.5,
//디폴트 표시
value: 1.5,
duration: _animationDuration,
);
7.8 Video UI
video_timeline_screen.dart
// 비디오가 끝나면 어떻게 처리할지의 함수
------------
void _onVideoFinished() {
return;
------------
//다음 비디오 자동재생
_pageController.nextPage(
duration: _scrollDuration,
curve: _scrollCurve,
);
}
video_post.dart
//VideoPlayerController이용하기 위한 함수
void _initVideoPlayer() async {
await _videoPlayerController.initialize();
-----------------
//한 영상 반복되게 해주는 파라메터
await _videoPlayerController.setLooping(true);
-----------------
//autoplay
// _videoPlayerController.play();
//VideoPlayerController를 어떻게 처리해줄 지 지정하는 listener
_videoPlayerController.addListener(_onVideoChange);
setState(() {});
FontAwesomeIcons.play,
color: Colors.white,
size: Sizes.size52,
),
),
),
),
),
),
-----------------
Positioned(
bottom: 30,
left: 30,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
"@jun",
style: TextStyle(
fontSize: Sizes.size20,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
Gaps.v10,
Text(
"This is my!!",
style: TextStyle(
fontSize: Sizes.size16,
color: Colors.white,
-----------------
),
)
],
),
),
],
),
);
}
}
Positioned(
bottom: 20,
right: 10,
child: Column(
children: const [
CircleAvatar(
radius: 25,
backgroundColor: Colors.black,
foregroundColor: Colors.white,
foregroundImage: NetworkImage(
"https://cdn.buymeacoffee.com/uploads/profile_pictures/2023/04/OX84VPRnUx1KYSpk.png@300w_0e.webp"),
child: Text("joon"),
),
],
),
),
],
),
);
}
}
반복 사용을 위해 아래 위젯 따로 보관
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
class VideoButton extends StatelessWidget {
final IconData icon;
final String text;
const VideoButton({
super.key,
required this.icon,
required this.text,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
FaIcon(
icon,
color: Colors.white,
size: Sizes.size36,
),
Gaps.v5,
Text(
text,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
);
}
}
child: Text("joon"),
),
---------------
Gaps.v24,
VideoButton(
icon: FontAwesomeIcons.solidHeart,
text: "2.9M",
),
Gaps.v24,
VideoButton(
icon: FontAwesomeIcons.solidComment,
text: "33K",
),
Gaps.v24,
VideoButton(
icon: FontAwesomeIcons.share,
text: "Share",
),
],
),
),
],
),
);
}
}
7.9 RefreshIndicator
// 비디오가 끝나면 어떻게 처리할지의 함수,보고 있지 않을 때도 캐쉬 살려두기 위함
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
---------------------
//유저가 당겨서 타임라인을 새로고침하는 함수
Future<void> _onRefresh() {
return Future.delayed(
const Duration(seconds: 5),
);
}
---------------------
@override
Widget build(BuildContext context) {
---------------------
//유저가 당겨서 타임라인을 새로고침하는 위젯
return RefreshIndicator(
onRefresh: _onRefresh,
---------------------
// 유용한 새 위젯
child: PageView.builder(
//스크린 넘어갈 때 느려지는 효과 없애는 파라미터
controller: _pageController,
하지만 문제는
1.아이폰 노치가 새로고침 마크 막고 있음
2.돌아갈 때 살짝 가려져있음
Widget build(BuildContext context) {
//유저가 당겨서 타임라인을 새로고침하는 위젯
return RefreshIndicator(
onRefresh: _onRefresh,
----------------
displacement: 50,
edgeOffset: 20,
color: Theme.of(context).primaryColor,
----------------
// 유용한 새 위젯
child: PageView.builder(
그래서 위치와 색깔도 바꾸고
main_navigation_screen의 build scaffold가 디폴트는 흰색이니까 이렇게 바꾸자
@override
Widget build(BuildContext context) {
return Scaffold(
------------------
backgroundColor: _selectedIndex == 0 ? Colors.black : Colors.white,
------------------
// // 선택된 탭의 페이지만 보여주기: 항상 새로운 페이지로써 표시됨
// body: screens.elementAt(_selectedIndex),
// offStage: 해당 위젯을 안 보이게 하면서 계속 존재하게 해주는 위젯
body: Stack(
여기서 아래 조건문 추가하기
"삭제"가 된 컨트롤러(VideoPlayerController)를 호출해서 생긴 에러가 생겼을 떄 "이미 삭제가 되어있는 상태" 가 생기는 걸 방지하기 위해
위젯이 트리에 있어서 mount되면 true를 반환할거니까 visibility에 변화가 있더라도, mount된 상태가 아니라면 아무것도 하지 않게 하는 조건문
//VideoPlayerController이용하기 위한 함수2, 보고 있지 않을 때도 캐쉬 살려두기 위함
@override
void dispose() {
_videoPlayerController.dispose();
super.dispose();
}
// 비디오가 보여지는 영역을 계산하고 한쪽에서만 영상 재생되도록 해주는 파라메터
void _onVisibilityChanged(VisibilityInfo info) {
-------------------
// mounted property: 위젯이 마운트되었는지 아닌지 알려줌
if (!mounted) return;
-------------------
if (info.visibleFraction == 1 &&
!_isPaused &&
!_videoPlayerController.value.isPlaying) {
_videoPlayerController.play();
}