Merge pull request #911 from GetStream/fix/full-screen-media-dispose

perf(ui): improve full screen media animation
This commit is contained in:
Salvatore Giordano
2022-02-28 13:13:20 +01:00
committed by GitHub
@@ -65,24 +65,35 @@ class FullScreenMedia extends StatefulWidget {
class _FullScreenMediaState extends State<FullScreenMedia> class _FullScreenMediaState extends State<FullScreenMedia>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
bool _optionsShown = true; late final AnimationController _animationController;
late final AnimationController _controller;
late final PageController _pageController; late final PageController _pageController;
late int _currentPage; late final _curvedAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeOut,
reverseCurve: Curves.easeIn,
);
final _opacityTween = Tween<double>(begin: 1, end: 0);
late final _opacityAnimation = _opacityTween.animate(
CurvedAnimation(
parent: _animationController,
curve: const Interval(0, 0.6, curve: Curves.easeOut),
),
);
late final ValueNotifier<int> _currentPage = ValueNotifier(widget.startIndex);
final videoPackages = <String, VideoPackage>{}; final videoPackages = <String, VideoPackage>{};
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_controller = AnimationController( _animationController = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
); );
_pageController = PageController(initialPage: widget.startIndex); _pageController = PageController(initialPage: widget.startIndex);
_currentPage = widget.startIndex;
for (var i = 0; i < widget.mediaAttachments.length; i++) { for (var i = 0; i < widget.mediaAttachments.length; i++) {
final attachment = widget.mediaAttachments[i]; final attachment = widget.mediaAttachments[i];
if (attachment.type != 'video') continue; if (attachment.type != 'video') continue;
@@ -116,41 +127,38 @@ class _FullScreenMediaState extends State<FullScreenMedia>
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
body: Stack( body: Stack(
children: [ children: [
AnimatedBuilder( PageView.builder(
animation: _controller, controller: _pageController,
builder: (context, snapshot) => PageView.builder( onPageChanged: (val) {
controller: _pageController, _currentPage.value = val;
onPageChanged: (val) {
setState(() {
_currentPage = val;
});
if (videoPackages.isEmpty) { if (videoPackages.isEmpty) {
return; return;
}
final currentAttachment = widget.mediaAttachments[val];
for (final e in videoPackages.values) {
if (e._attachment != currentAttachment) {
e._chewieController?.pause();
} }
}
final currentAttachment = widget.mediaAttachments[val]; if (widget.autoplayVideos &&
currentAttachment.type == 'video') {
for (final e in videoPackages.values) { final controller = videoPackages[currentAttachment.id]!;
if (e._attachment != currentAttachment) { controller._chewieController?.play();
e._chewieController?.pause(); }
} },
} itemBuilder: (context, index) {
final attachment = widget.mediaAttachments[index];
if (widget.autoplayVideos && if (attachment.type == 'image' || attachment.type == 'giphy') {
currentAttachment.type == 'video') { final imageUrl = attachment.imageUrl ??
final controller = videoPackages[currentAttachment.id]!; attachment.assetUrl ??
controller._chewieController?.play(); attachment.thumbUrl;
} return AnimatedBuilder(
}, animation: _curvedAnimation,
itemBuilder: (context, index) { builder: (context, child) => PhotoView(
final attachment = widget.mediaAttachments[index];
if (attachment.type == 'image' ||
attachment.type == 'giphy') {
final imageUrl = attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl;
return PhotoView(
loadingBuilder: (context, image) => const Offstage(), loadingBuilder: (context, image) => const Offstage(),
imageProvider: (imageUrl == null && imageProvider: (imageUrl == null &&
attachment.localUri != null && attachment.localUri != null &&
@@ -166,97 +174,91 @@ class _FullScreenMediaState extends State<FullScreenMedia>
color: ColorTween( color: ColorTween(
begin: ChannelHeaderTheme.of(context).color, begin: ChannelHeaderTheme.of(context).color,
end: Colors.black, end: Colors.black,
).lerp(_controller.value), ).lerp(_curvedAnimation.value),
), ),
onTapUp: (a, b, c) { onTapUp: (a, b, c) {
setState(() { if (_animationController.isCompleted) {
_optionsShown = !_optionsShown; _animationController.reverse();
});
if (_controller.isCompleted) {
_controller.reverse();
} else { } else {
_controller.forward(); _animationController.forward();
} }
}, },
); ),
} else if (attachment.type == 'video') { );
final controller = videoPackages[attachment.id]!; } else if (attachment.type == 'video') {
if (!controller.initialized) { final controller = videoPackages[attachment.id]!;
return const Center( if (!controller.initialized) {
child: CircularProgressIndicator(), return const Center(
); child: CircularProgressIndicator(),
}
return InkWell(
onTap: () {
setState(() {
_optionsShown = !_optionsShown;
});
if (_controller.isCompleted) {
_controller.reverse();
} else {
_controller.forward();
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 50,
),
child: Chewie(
controller: controller.chewieController!,
),
),
); );
} }
return Container(); return InkWell(
}, onTap: () {
itemCount: widget.mediaAttachments.length, if (_animationController.isCompleted) {
), _animationController.reverse();
), } else {
AnimatedOpacity( _animationController.forward();
opacity: _optionsShown ? 1.0 : 0.0, }
duration: const Duration(milliseconds: 300), },
child: Column( child: Padding(
mainAxisAlignment: MainAxisAlignment.spaceBetween, padding: const EdgeInsets.symmetric(
children: [ vertical: 50,
GalleryHeader( ),
userName: widget.userName, child: Chewie(
sentAt: context.translations.sentAtText( controller: controller.chewieController!,
date: widget.message.createdAt, ),
time: widget.message.createdAt,
), ),
onBackPressed: () { );
Navigator.of(context).pop(); }
}, return const SizedBox();
message: widget.message, },
currentIndex: _currentPage, itemCount: widget.mediaAttachments.length,
onShowMessage: () { ),
widget.onShowMessage?.call( FadeTransition(
widget.message, opacity: _opacityAnimation,
StreamChannel.of(context).channel, child: ValueListenableBuilder<int>(
); valueListenable: _currentPage,
}, builder: (context, value, child) => Column(
attachmentActionsModalBuilder: mainAxisAlignment: MainAxisAlignment.spaceBetween,
widget.attachmentActionsModalBuilder, children: [
), GalleryHeader(
if (!widget.message.isEphemeral) userName: widget.userName,
GalleryFooter( sentAt: context.translations.sentAtText(
currentPage: _currentPage, date: widget.message.createdAt,
totalPages: widget.mediaAttachments.length, time: widget.message.createdAt,
mediaAttachments: widget.mediaAttachments, ),
onBackPressed: () {
Navigator.of(context).pop();
},
message: widget.message, message: widget.message,
mediaSelectedCallBack: (val) { currentIndex: value,
setState(() { onShowMessage: () {
_currentPage = val; widget.onShowMessage?.call(
widget.message,
StreamChannel.of(context).channel,
);
},
attachmentActionsModalBuilder:
widget.attachmentActionsModalBuilder,
),
if (!widget.message.isEphemeral)
GalleryFooter(
currentPage: value,
totalPages: widget.mediaAttachments.length,
mediaAttachments: widget.mediaAttachments,
message: widget.message,
mediaSelectedCallBack: (val) {
_currentPage.value = val;
_pageController.animateToPage( _pageController.animateToPage(
val, val,
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
Navigator.pop(context); Navigator.pop(context);
}); },
}, ),
), ],
], ),
), ),
), ),
], ],
@@ -264,9 +266,11 @@ class _FullScreenMediaState extends State<FullScreenMedia>
); );
@override @override
void dispose() async { void dispose() {
_animationController.dispose();
_pageController.dispose();
for (final package in videoPackages.values) { for (final package in videoPackages.values) {
await package.dispose(); package.dispose();
} }
super.dispose(); super.dispose();
} }