Merge branch 'GetStream:develop' into develop

This commit is contained in:
Simon Eckerstorfer
2022-03-01 17:28:57 +01:00
committed by GitHub
7 changed files with 217 additions and 184 deletions
+3
View File
@@ -10,6 +10,9 @@
- [[#890]](https://github.com/GetStream/stream-chat-flutter/pull/890) Fixed Reactions not updating on thread messages. - [[#890]](https://github.com/GetStream/stream-chat-flutter/pull/890) Fixed Reactions not updating on thread messages.
Thanks [bstolinski](https://github.com/bstolinski). Thanks [bstolinski](https://github.com/bstolinski).
- [[#897]](https://github.com/GetStream/stream-chat-flutter/issues/897) Fixed error type mis-match in `AuthInterceptor`. - [[#897]](https://github.com/GetStream/stream-chat-flutter/issues/897) Fixed error type mis-match in `AuthInterceptor`.
- [[#891]](https://github.com/GetStream/stream-chat-flutter/pull/891) Fixed reply counter for parent message not
updating correctly after deleting thread message.
- Fix `channelState.copyWith` with respect to pinnedMessages.
## 3.4.0 ## 3.4.0
@@ -407,7 +407,7 @@ class Channel {
if (index != -1) { if (index != -1) {
final newAttachments = [...message!.attachments]..[index] = attachment; final newAttachments = [...message!.attachments]..[index] = attachment;
final updatedMessage = message!.copyWith(attachments: newAttachments); final updatedMessage = message!.copyWith(attachments: newAttachments);
state?.addMessage(updatedMessage); state?.updateMessage(updatedMessage);
// updating original message for next iteration // updating original message for next iteration
message = message!.merge(updatedMessage); message = message!.merge(updatedMessage);
} }
@@ -512,7 +512,7 @@ class Channel {
).toList(), ).toList(),
); );
state!.addMessage(message); state!.updateMessage(message);
try { try {
if (message.attachments.any((it) => !it.uploadState.isSuccess)) { if (message.attachments.any((it) => !it.uploadState.isSuccess)) {
@@ -535,7 +535,7 @@ class Channel {
type, type,
skipPush: skipPush, skipPush: skipPush,
); );
state!.addMessage(response.message); state!.updateMessage(response.message);
if (cooldown > 0) cooldownStartedAt = DateTime.now(); if (cooldown > 0) cooldownStartedAt = DateTime.now();
return response; return response;
} catch (e) { } catch (e) {
@@ -571,7 +571,7 @@ class Channel {
).toList(), ).toList(),
); );
state?.addMessage(message); state?.updateMessage(message);
try { try {
if (message.attachments.any((it) => !it.uploadState.isSuccess)) { if (message.attachments.any((it) => !it.uploadState.isSuccess)) {
@@ -594,7 +594,7 @@ class Channel {
ownReactions: message.ownReactions, ownReactions: message.ownReactions,
); );
state?.addMessage(m); state?.updateMessage(m);
return response; return response;
} catch (e) { } catch (e) {
@@ -602,7 +602,7 @@ class Channel {
if (e.isRetriable) { if (e.isRetriable) {
state!._retryQueue.add([message]); state!._retryQueue.add([message]);
} else { } else {
state?.addMessage(originalMessage); state?.updateMessage(originalMessage);
} }
} }
rethrow; rethrow;
@@ -630,7 +630,7 @@ class Channel {
ownReactions: message.ownReactions, ownReactions: message.ownReactions,
); );
state?.addMessage(updatedMessage); state?.updateMessage(updatedMessage);
return response; return response;
} catch (e) { } catch (e) {
@@ -643,13 +643,18 @@ class Channel {
/// Deletes the [message] from the channel. /// Deletes the [message] from the channel.
Future<EmptyResponse> deleteMessage(Message message, {bool? hard}) async { Future<EmptyResponse> deleteMessage(Message message, {bool? hard}) async {
final hardDelete = hard ?? false;
// Directly deleting the local messages which are not yet sent to server // Directly deleting the local messages which are not yet sent to server
if (message.status == MessageSendingStatus.sending || if (message.status == MessageSendingStatus.sending ||
message.status == MessageSendingStatus.failed) { message.status == MessageSendingStatus.failed) {
state!.addMessage(message.copyWith( state!.deleteMessage(
type: 'deleted', message.copyWith(
status: MessageSendingStatus.sent, type: 'deleted',
)); status: MessageSendingStatus.sent,
),
hardDelete: hardDelete,
);
// Removing the attachments upload completer to stop the `sendMessage` // Removing the attachments upload completer to stop the `sendMessage`
// waiting for attachments to complete. // waiting for attachments to complete.
@@ -667,11 +672,14 @@ class Channel {
deletedAt: message.deletedAt ?? DateTime.now(), deletedAt: message.deletedAt ?? DateTime.now(),
); );
state?.addMessage(message); state?.deleteMessage(message, hardDelete: hardDelete);
final response = await _client.deleteMessage(message.id, hard: hard); final response = await _client.deleteMessage(message.id, hard: hard);
state?.addMessage(message.copyWith(status: MessageSendingStatus.sent)); state?.deleteMessage(
message.copyWith(status: MessageSendingStatus.sent),
hardDelete: hardDelete,
);
return response; return response;
} catch (e) { } catch (e) {
@@ -861,7 +869,7 @@ class Channel {
ownReactions: ownReactions, ownReactions: ownReactions,
); );
state?.addMessage(newMessage); state?.updateMessage(newMessage);
try { try {
final reactionResp = await _client.sendReaction( final reactionResp = await _client.sendReaction(
@@ -874,7 +882,7 @@ class Channel {
return reactionResp; return reactionResp;
} catch (_) { } catch (_) {
// Reset the message if the update fails // Reset the message if the update fails
state?.addMessage(message); state?.updateMessage(message);
rethrow; rethrow;
} }
} }
@@ -914,7 +922,7 @@ class Channel {
ownReactions: ownReactions, ownReactions: ownReactions,
); );
state?.addMessage(newMessage); state?.updateMessage(newMessage);
try { try {
final deleteResponse = await _client.deleteReaction( final deleteResponse = await _client.deleteReaction(
@@ -924,7 +932,7 @@ class Channel {
return deleteResponse; return deleteResponse;
} catch (_) { } catch (_) {
// Reset the message if the update fails // Reset the message if the update fails
state?.addMessage(message); state?.updateMessage(message);
rethrow; rethrow;
} }
} }
@@ -1081,7 +1089,7 @@ class Channel {
// update the passed message with response message // update the passed message with response message
if (res.message != null) { if (res.message != null) {
state!.addMessage(res.message!); state!.updateMessage(res.message!);
} else { } else {
// remove the passed message if response does // remove the passed message if response does
// not contain message // not contain message
@@ -1712,7 +1720,7 @@ class ChannelClientState {
final message = event.message!.copyWith( final message = event.message!.copyWith(
ownReactions: ownReactions, ownReactions: ownReactions,
); );
addMessage(message); updateMessage(message);
})); }));
} }
@@ -1725,7 +1733,7 @@ class ChannelClientState {
final message = event.message!.copyWith( final message = event.message!.copyWith(
ownReactions: oldMessage?.ownReactions, ownReactions: oldMessage?.ownReactions,
); );
addMessage(message); updateMessage(message);
})); }));
} }
@@ -1743,7 +1751,7 @@ class ChannelClientState {
final message = event.message!.copyWith( final message = event.message!.copyWith(
ownReactions: oldMessage?.ownReactions, ownReactions: oldMessage?.ownReactions,
); );
addMessage(message); updateMessage(message);
if (message.pinned) { if (message.pinned) {
_channelState = _channelState.copyWith( _channelState = _channelState.copyWith(
@@ -1760,9 +1768,9 @@ class ChannelClientState {
_subscriptions.add(_channel.on(EventType.messageDeleted).listen((event) { _subscriptions.add(_channel.on(EventType.messageDeleted).listen((event) {
final message = event.message!; final message = event.message!;
if (event.hardDelete == true) { if (event.hardDelete == true) {
removeMessage(message, hardDelete: true); removeMessage(message);
} else { } else {
addMessage(message); updateMessage(message);
} }
})); }));
} }
@@ -1777,7 +1785,7 @@ class ChannelClientState {
final message = event.message!; final message = event.message!;
if (isUpToDate || if (isUpToDate ||
(message.parentId != null && message.showInChannel != true)) { (message.parentId != null && message.showInChannel != true)) {
addMessage(message); updateMessage(message);
} }
if (_countMessageAsUnread(message)) { if (_countMessageAsUnread(message)) {
@@ -1787,9 +1795,13 @@ class ChannelClientState {
} }
/// Add a [message] to this [channelState]. /// Add a [message] to this [channelState].
void addMessage(Message message) { @Deprecated('Use updateMessage instead')
void addMessage(Message message) => updateMessage(message);
/// Updates the [message] in the state if it exists. Adds it otherwise.
void updateMessage(Message message) {
if (message.parentId == null || message.showInChannel == true) { if (message.parentId == null || message.showInChannel == true) {
final newMessages = List<Message>.from(_channelState.messages); final newMessages = [...messages];
final oldIndex = newMessages.indexWhere((m) => m.id == message.id); final oldIndex = newMessages.indexWhere((m) => m.id == message.id);
if (oldIndex != -1) { if (oldIndex != -1) {
Message? m; Message? m;
@@ -1804,8 +1816,24 @@ class ChannelClientState {
newMessages.add(message); newMessages.add(message);
} }
final newPinnedMessages = [...pinnedMessages];
final oldPinnedIndex =
newPinnedMessages.indexWhere((m) => m.id == message.id);
// Handle pinned messages
if (message.pinned) {
if (oldPinnedIndex != -1) {
newPinnedMessages[oldPinnedIndex] = message;
} else {
newPinnedMessages.add(message);
}
} else {
newPinnedMessages.removeWhere((m) => m.id == message.id);
}
_channelState = _channelState.copyWith( _channelState = _channelState.copyWith(
messages: newMessages..sort(_sortByCreatedAt), messages: newMessages..sort(_sortByCreatedAt),
pinnedMessages: newPinnedMessages,
channel: _channelState.channel?.copyWith( channel: _channelState.channel?.copyWith(
lastMessageAt: message.createdAt, lastMessageAt: message.createdAt,
), ),
@@ -1818,41 +1846,35 @@ class ChannelClientState {
} }
/// Remove a [message] from this [channelState]. /// Remove a [message] from this [channelState].
void removeMessage(Message message, {bool hardDelete = false}) { void removeMessage(Message message) {
final parentId = message.parentId; final parentId = message.parentId;
// i.e. it's a thread message // i.e. it's a thread message, Remove it
// 1. Remove the thread message
// 2. Reduce total reply count of parent message
if (parentId != null) { if (parentId != null) {
final allMessages = [...messages]; final newThreads = {...threads};
final parentMessage = allMessages.firstWhereOrNull( // Early return in case the thread is not available
(it) => it.id == parentId, if (!newThreads.containsKey(parentId)) return;
);
// return if message not available in the memory _threads = newThreads
if (parentMessage == null) return; ..update(
final replyCount = parentMessage.replyCount; parentId,
// return if reply count is null or zero (messages) => messages..removeWhere((e) => e.id == message.id),
if (replyCount == null || replyCount == 0) return; );
addMessage(parentMessage.copyWith(replyCount: replyCount - 1)); // Early return if the thread message is not shown in channel.
updateThreadInfo( if (message.showInChannel == false) return;
parentId,
threads[parentId]!
..removeWhere(
(e) => e.id == message.id,
),
);
} else {
// Remove regular message
final allMessages = [...messages];
if (hardDelete) {
allMessages.removeWhere((e) => e.id == message.id);
_channelState = _channelState.copyWith(messages: allMessages);
} else if (allMessages.remove(message)) {
_channelState = _channelState.copyWith(messages: allMessages);
}
} }
// Remove regular message, thread message shown in channel
final allMessages = [...messages];
_channelState = _channelState.copyWith(
messages: allMessages..removeWhere((e) => e.id == message.id),
);
}
/// Removes/Updates the [message] based on the [hardDelete] value.
void deleteMessage(Message message, {bool hardDelete = false}) {
if (hardDelete) return removeMessage(message);
return updateMessage(message);
} }
void _listenReadEvents() { void _listenReadEvents() {
@@ -1898,11 +1920,12 @@ class ChannelClientState {
.distinct(const ListEquality().equals); .distinct(const ListEquality().equals);
/// Channel pinned message list. /// Channel pinned message list.
List<Message> get pinnedMessages => _channelState.pinnedMessages.toList(); List<Message> get pinnedMessages => _channelState.pinnedMessages;
/// Channel pinned message list as a stream. /// Channel pinned message list as a stream.
Stream<List<Message>> get pinnedMessagesStream => Stream<List<Message>> get pinnedMessagesStream => channelStateStream
channelStateStream.map((cs) => cs.pinnedMessages.toList()); .map((cs) => cs.pinnedMessages)
.distinct(const ListEquality().equals);
/// Get channel last message. /// Get channel last message.
Message? get lastMessage => Message? get lastMessage =>
@@ -2213,7 +2236,7 @@ class ChannelClientState {
.toList(); .toList();
updateChannelState(_channelState.copyWith( updateChannelState(_channelState.copyWith(
pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(), pinnedMessages: pinnedMessages.where(_pinIsValid).toList(),
messages: expiredMessages, messages: expiredMessages,
)); ));
} }
@@ -2250,7 +2273,7 @@ class ChannelClientState {
} }
} }
bool Function(Message) _pinIsValid() { bool _pinIsValid(Message message) {
final now = DateTime.now(); final now = DateTime.now();
return (Message m) => m.pinExpires!.isAfter(now); return message.pinExpires!.isAfter(now);
} }
@@ -158,7 +158,7 @@ class RetryQueue {
: message.status == MessageSendingStatus.updating : message.status == MessageSendingStatus.updating
? MessageSendingStatus.failed_update ? MessageSendingStatus.failed_update
: MessageSendingStatus.failed_delete; : MessageSendingStatus.failed_delete;
channel.state?.addMessage(message.copyWith(status: newStatus)); channel.state?.updateMessage(message.copyWith(status: newStatus));
} }
Future<void> _retryMessage(Message message) async { Future<void> _retryMessage(Message message) async {
@@ -56,7 +56,7 @@ class ChannelState {
ChannelModel? channel, ChannelModel? channel,
List<Message>? messages, List<Message>? messages,
List<Member>? members, List<Member>? members,
List<Message>? pinnedMessages, List<Message> pinnedMessages = _emptyPinnedMessages,
int? watcherCount, int? watcherCount,
List<User>? watchers, List<User>? watchers,
List<Read>? read, List<Read>? read,
@@ -69,7 +69,7 @@ class ChannelState {
// FIXME: Use non-nullable by default instead of empty list. // FIXME: Use non-nullable by default instead of empty list.
pinnedMessages: pinnedMessages == _emptyPinnedMessages pinnedMessages: pinnedMessages == _emptyPinnedMessages
? this.pinnedMessages ? this.pinnedMessages
: pinnedMessages ?? _emptyPinnedMessages, : pinnedMessages,
watcherCount: watcherCount ?? this.watcherCount, watcherCount: watcherCount ?? this.watcherCount,
watchers: watchers ?? this.watchers, watchers: watchers ?? this.watchers,
read: read ?? this.read, read: read ?? this.read,
@@ -5,6 +5,7 @@
- [[#888]](https://github.com/GetStream/stream-chat-flutter/issues/888) Fix `unban` command not working in `MessageInput`. - [[#888]](https://github.com/GetStream/stream-chat-flutter/issues/888) Fix `unban` command not working in `MessageInput`.
- [[#805]](https://github.com/GetStream/stream-chat-flutter/issues/805) Updated chewie dependency version to 1.3.0 - [[#805]](https://github.com/GetStream/stream-chat-flutter/issues/805) Updated chewie dependency version to 1.3.0
- Fix `showScrollToBottom` in `MessageListView` not respecting false value. - Fix `showScrollToBottom` in `MessageListView` not respecting false value.
- Fix default `Channel` route not opening from `ChannelListView` when `ChannelAvatar` is tapped
✅ Added ✅ Added
@@ -599,7 +599,9 @@ class _ChannelListViewState extends State<ChannelListView> {
child: ChannelPreview( child: ChannelPreview(
onLongPress: widget.onChannelLongPress, onLongPress: widget.onChannelLongPress,
channel: channel, channel: channel,
onImageTap: () => widget.onImageTap?.call(channel), onImageTap: widget.onImageTap != null
? () => widget.onImageTap!(channel)
: null,
onTap: (channel) => onTap(channel, widget.channelWidget), onTap: (channel) => onTap(channel, widget.channelWidget),
), ),
), ),
@@ -612,7 +614,7 @@ class _ChannelListViewState extends State<ChannelListView> {
if (widget.onChannelTap != null) { if (widget.onChannelTap != null) {
onTap = widget.onChannelTap!; onTap = widget.onChannelTap!;
} else { } else {
onTap = (client, _) { onTap = (channel, _) {
if (widget.channelWidget == null) { if (widget.channelWidget == null) {
return; return;
} }
@@ -620,7 +622,7 @@ class _ChannelListViewState extends State<ChannelListView> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => StreamChannel( builder: (context) => StreamChannel(
channel: client, channel: channel,
child: widget.channelWidget!, child: widget.channelWidget!,
), ),
), ),
@@ -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();
} }