diff --git a/lib/controller/story_controller.dart b/lib/controller/story_controller.dart index 9636aa75..55eb2c8b 100644 --- a/lib/controller/story_controller.dart +++ b/lib/controller/story_controller.dart @@ -1,6 +1,7 @@ import 'package:rxdart/rxdart.dart'; +import 'package:flutter/foundation.dart'; -enum PlaybackState { pause, play, next, previous } +enum PlaybackState { pause, play, next, previous, remove } /// Controller to sync playback between animated child (story) views. This /// helps make sure when stories are paused, the animation (gifs/slides) are @@ -29,6 +30,11 @@ class StoryController { playbackNotifier.add(PlaybackState.previous); } + /// Call this to remove the current story from the StoryView. + void removeCurrentStory() { + playbackNotifier.add(PlaybackState.remove); + } + /// Remember to call dispose when the story screen is disposed to close /// the notifier stream. void dispose() { diff --git a/lib/widgets/story_view.dart b/lib/widgets/story_view.dart index b7f6f1fe..83e7b485 100644 --- a/lib/widgets/story_view.dart +++ b/lib/widgets/story_view.dart @@ -79,10 +79,11 @@ class StoryItem { bottom: Radius.circular(roundedBottom ? 8 : 0), ), ), - padding: textOuterPadding?? EdgeInsets.symmetric( - horizontal: 24, - vertical: 16, - ), + padding: textOuterPadding ?? + EdgeInsets.symmetric( + horizontal: 24, + vertical: 16, + ), child: Center( child: Text( title, @@ -140,12 +141,13 @@ class StoryItem { margin: EdgeInsets.only( bottom: 24, ), - padding: captionOuterPadding?? EdgeInsets.symmetric( - horizontal: 24, - vertical: 8, - ), + padding: captionOuterPadding ?? + EdgeInsets.symmetric( + horizontal: 24, + vertical: 8, + ), color: caption != null ? Colors.black54 : Colors.transparent, - child: caption?? const SizedBox.shrink(), + child: caption ?? const SizedBox.shrink(), ), ), ) @@ -193,11 +195,12 @@ class StoryItem { ), Container( margin: EdgeInsets.only(bottom: 16), - padding: captionOuterPadding?? EdgeInsets.symmetric(horizontal: 24, vertical: 8), + padding: captionOuterPadding ?? + EdgeInsets.symmetric(horizontal: 24, vertical: 8), child: Align( alignment: Alignment.bottomLeft, child: Container( - child: caption?? const SizedBox.shrink(), + child: caption ?? const SizedBox.shrink(), width: double.infinity, ), ), @@ -252,7 +255,7 @@ class StoryItem { padding: EdgeInsets.symmetric(horizontal: 24, vertical: 8), color: caption != null ? Colors.black54 : Colors.transparent, - child: caption?? const SizedBox.shrink(), + child: caption ?? const SizedBox.shrink(), ), ), ) @@ -406,6 +409,7 @@ class StoryView extends StatefulWidget { /// Indicator Color final Color? indicatorColor; + /// Indicator Foreground Color final Color? indicatorForegroundColor; @@ -427,7 +431,10 @@ class StoryView extends StatefulWidget { this.indicatorColor, this.indicatorForegroundColor, this.indicatorHeight = IndicatorHeight.large, - this.indicatorOuterPadding = const EdgeInsets.symmetric(horizontal: 16, vertical: 8,), + this.indicatorOuterPadding = const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), }); @override @@ -445,6 +452,37 @@ class StoryViewState extends State with TickerProviderStateMixin { VerticalDragInfo? verticalDragInfo; + /// Removes the current story from the list and resets playback to the next story. + void removeCurrentStory() { + final currentStory = _currentStory; + if (currentStory == null) return; + // If only one story remains, just complete playback and do not remove + if (widget.storyItems.length == 1) { + _onComplete(); + return; + } + final currentIndex = widget.storyItems.indexOf(currentStory); + final wasLastStory = (currentIndex == widget.storyItems.length - 1); + setState(() { + widget.storyItems.removeAt(currentIndex); + }); + // If there are no stories left, just stop playback + if (widget.storyItems.isEmpty) { + _animationController?.stop(); + return; + } + // If we just removed the last story, call onComplete + if (wasLastStory) { + _onComplete(); + return; + } + // Reset all stories after the removed index to not shown + for (int i = currentIndex; i < widget.storyItems.length; i++) { + widget.storyItems[i]!.shown = false; + } + _beginPlay(); + } + StoryItem? get _currentStory { return widget.storyItems.firstWhereOrNull((it) => !it!.shown); } @@ -495,6 +533,10 @@ class StoryViewState extends State with TickerProviderStateMixin { _removeNextHold(); _goBack(); break; + // Support removal via playbackNotifier + case PlaybackState.remove: + removeCurrentStory(); + break; } }); @@ -796,8 +838,11 @@ class PageBarState extends State { right: widget.pages.last == it ? 0 : this.spacing), child: StoryProgressIndicator( isPlaying(it) ? widget.animation!.value : (it.shown ? 1 : 0), - indicatorHeight: - widget.indicatorHeight == IndicatorHeight.large ? 5 : widget.indicatorHeight == IndicatorHeight.medium ? 3 : 2, + indicatorHeight: widget.indicatorHeight == IndicatorHeight.large + ? 5 + : widget.indicatorHeight == IndicatorHeight.medium + ? 3 + : 2, indicatorColor: widget.indicatorColor, indicatorForegroundColor: widget.indicatorForegroundColor, ), @@ -831,11 +876,11 @@ class StoryProgressIndicator extends StatelessWidget { this.indicatorHeight, ), foregroundPainter: IndicatorOval( - this.indicatorForegroundColor?? Colors.white.withValues(alpha: 0.8), + this.indicatorForegroundColor ?? Colors.white.withValues(alpha: 0.8), this.value, ), painter: IndicatorOval( - this.indicatorColor?? Colors.white.withValues(alpha: 0.4), + this.indicatorColor ?? Colors.white.withValues(alpha: 0.4), 1.0, ), ); diff --git a/pubspec.lock b/pubspec.lock index 2df744d0..7c25fef6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -316,10 +316,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" video_player: dependency: "direct main" description: