From 0ddde96352399bb083674ca61d247ddfd0af3e29 Mon Sep 17 00:00:00 2001 From: Cagatay Ulusoy Date: Sun, 28 Jul 2024 14:36:21 +0300 Subject: [PATCH] Add possibility to set pagination duration and prevent crashes during pagination --- README.md | 1 + .../wolt_modal_sheet_animated_switcher.dart | 120 +++++++++--------- .../wolt_modal_sheet_animation_style.dart | 4 + lib/src/wolt_modal_sheet.dart | 2 - 4 files changed, 68 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index c197360d..364cec5e 100644 --- a/README.md +++ b/README.md @@ -891,6 +891,7 @@ providing an instance of `WoltModalSheetAnimationStyle` class to WoltModalSheetThemeData( animationStyle: WoltModalSheetAnimationStyle( paginationAnimationStyle: WoltModalSheetPaginationAnimationStyle( + paginationDuration = Duration(milliseconds: 250), mainContentIncomingOpacityCurve: const Interval( 150 / 350, 350 / 350, diff --git a/lib/src/content/wolt_modal_sheet_animated_switcher.dart b/lib/src/content/wolt_modal_sheet_animated_switcher.dart index a85c7c90..16926880 100644 --- a/lib/src/content/wolt_modal_sheet_animated_switcher.dart +++ b/lib/src/content/wolt_modal_sheet_animated_switcher.dart @@ -105,7 +105,7 @@ class _WoltModalSheetAnimatedSwitcherState bool _isForwardMove = true; - bool? _shouldAnimatePagination; + late bool _shouldAnimatePagination; GlobalKey get _pageTitleKey => _titleKeys[_pageIndex]; @@ -206,47 +206,45 @@ class _WoltModalSheetAnimatedSwitcherState final outgoingWidgets = _outgoingPageWidgets; final animationController = _animationController; final animatePagination = _shouldAnimatePagination; - - return Stack( - alignment: Alignment.bottomCenter, - children: [ - if (outgoingWidgets != null) - WoltModalSheetLayout( - paginatingWidgetsGroup: outgoingWidgets, - page: _page, - woltModalType: widget.woltModalType, - showDragHandle: widget.showDragHandle, - ), - if (incomingWidgets != null) - WoltModalSheetLayout( - paginatingWidgetsGroup: incomingWidgets, - page: _page, - woltModalType: widget.woltModalType, - showDragHandle: widget.showDragHandle, - ), - if (incomingWidgets != null && - animationController != null && - animatePagination != null && - animatePagination && - animationController.value != 1.0) - Offstage( - child: KeyedSubtree( - key: _incomingOffstagedMainContentKeys[_pageIndex], - child: incomingWidgets.offstagedMainContent, + final isAnimating = animationController != null && + animatePagination && + animationController.isAnimating && + animationController.value != 1.0; + return AbsorbPointer( + absorbing: isAnimating, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + if (outgoingWidgets != null) + WoltModalSheetLayout( + paginatingWidgetsGroup: outgoingWidgets, + page: _page, + woltModalType: widget.woltModalType, + showDragHandle: widget.showDragHandle, ), - ), - if (outgoingWidgets != null && - animationController != null && - animatePagination != null && - animatePagination && - animationController.value != 1.0) - Offstage( - child: KeyedSubtree( - key: _outgoingOffstagedMainContentKeys[_pageIndex], - child: outgoingWidgets.offstagedMainContent, + if (incomingWidgets != null) + WoltModalSheetLayout( + paginatingWidgetsGroup: incomingWidgets, + page: _page, + woltModalType: widget.woltModalType, + showDragHandle: widget.showDragHandle, ), - ), - ], + if (incomingWidgets != null && isAnimating) + Offstage( + child: KeyedSubtree( + key: _incomingOffstagedMainContentKeys[_pageIndex], + child: incomingWidgets.offstagedMainContent, + ), + ), + if (outgoingWidgets != null && isAnimating) + Offstage( + child: KeyedSubtree( + key: _outgoingOffstagedMainContentKeys[_pageIndex], + child: outgoingWidgets.offstagedMainContent, + ), + ), + ], + ), ); } @@ -270,27 +268,23 @@ class _WoltModalSheetAnimatedSwitcherState // We set the _shouldAnimatePagination to animate, which dictates whether the new page transition will be animated. _shouldAnimatePagination = animate; + final themeData = Theme.of(context).extension(); + final defaultThemeData = WoltModalSheetDefaultThemeData(context); + final WoltModalSheetAnimationStyle animationStyle = + themeData?.animationStyle ?? defaultThemeData.animationStyle; // An AnimationController is created and attached to this State object (with 'this' as the vsync). _animationController = AnimationController( - duration: const Duration( - milliseconds: defaultWoltModalTransitionAnimationDuration), + duration: animationStyle.paginationAnimationStyle.paginationDuration, vsync: this, - ) - // We also attach a status listener to the animation controller. When the animation is completed, it will trigger a state change. - ..addStatusListener((status) { + )..addStatusListener((status) { if (status == AnimationStatus.completed) { - setState(() { - _shouldAnimatePagination = null; - // We clear the _outgoingPageWidgets, which was storing the "outgoing" page (the page we're transitioning from) - _outgoingPageWidgets = null; - // We ensure that the animation controller's value is set to its upper bound (which should be 1.0) - _animationController?.value = - _animationController?.upperBound ?? 1.0; - // We dispose of the animation controller to free up resources as we're done with this animation - _animationController?.dispose(); - // We also set the animation controller reference to null as it's no longer needed. - _animationController = null; - }); + // If the widget is disposed while the animation is still running calling setState + // will throw an exception. + if (context.mounted) { + setState(() => _onPaginationAnimationComplete()); + } else { + _onPaginationAnimationComplete(); + } } }); @@ -317,6 +311,18 @@ class _WoltModalSheetAnimatedSwitcherState } } + void _onPaginationAnimationComplete() { + _shouldAnimatePagination = false; + // We clear the _outgoingPageWidgets, which was storing the "outgoing" page (the page we're transitioning from) + _outgoingPageWidgets = null; + // We ensure that the animation controller's value is set to its upper bound (which should be 1.0) + _animationController?.value = _animationController?.upperBound ?? 1.0; + // We dispose of the animation controller to free up resources as we're done with this animation + _animationController?.dispose(); + // We also set the animation controller reference to null as it's no longer needed. + _animationController = null; + } + PaginatingWidgetsGroup _createIncomingWidgets( AnimationController animationController) { final themeData = Theme.of(context).extension(); diff --git a/lib/src/theme/wolt_modal_sheet_animation_style.dart b/lib/src/theme/wolt_modal_sheet_animation_style.dart index 951d4dff..2fe767e8 100644 --- a/lib/src/theme/wolt_modal_sheet_animation_style.dart +++ b/lib/src/theme/wolt_modal_sheet_animation_style.dart @@ -194,6 +194,9 @@ class WoltModalSheetPaginationAnimationStyle { /// Determines the final off-screen position of the exiting content, controlling its exit trajectory. final Offset? outgoingMainContentSlideEndOffset; + /// The duration of the pagination transition. + final Duration paginationDuration; + /// Constructs a [WoltModalSheetPaginationAnimationStyle] with customizable animation curves /// and offsets for different components during page transitions. Allows for detailed control /// over the appearance and behavior of modal sheet pagination animations, enabling developers @@ -202,6 +205,7 @@ class WoltModalSheetPaginationAnimationStyle { /// Default values for curves and offsets are provided, but all can be overridden to achieve /// custom animation effects tailored to specific design requirements. const WoltModalSheetPaginationAnimationStyle({ + this.paginationDuration = const Duration(milliseconds: 350), this.incomingMainContentSlideBeginOffset, this.incomingMainContentSlideEndOffset = Offset.zero, this.outgoingMainContentSlideBeginOffset = Offset.zero, diff --git a/lib/src/wolt_modal_sheet.dart b/lib/src/wolt_modal_sheet.dart index f6eeb364..02a089d0 100644 --- a/lib/src/wolt_modal_sheet.dart +++ b/lib/src/wolt_modal_sheet.dart @@ -8,8 +8,6 @@ import 'package:wolt_modal_sheet/src/widgets/wolt_animated_modal_barrier.dart'; import 'package:wolt_modal_sheet/src/widgets/wolt_modal_sheet_content_gesture_detector.dart'; import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; -const int defaultWoltModalTransitionAnimationDuration = 350; - /// Signature for a function that builds a list of [SliverWoltModalSheetPage] based on the given [BuildContext]. typedef WoltModalSheetPageListBuilder = List Function( BuildContext context,