diff --git a/README.md b/README.md index a28facbc..d2b57ec4 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,56 @@ Here is an example that shows all the modal sheet elements in use: ![Modal sheet elements in use](https://github.com/woltapp/wolt_modal_sheet/blob/main/doc/bottom_sheet_example.jpeg?raw=true) +## Customizable Animations + +Developers can customize the page scrolling and pagination animations by +providing an instance of `WoltModalSheetAnimationStyle` class to +`WoltModalSheetThemeData`. + +### Default Animation Style Specifications + +#### Pagination Animation + +![Modal sheet elements in use](https://github.com/woltapp/wolt_modal_sheet/blob/main/doc/pagination_modal_sheet.png?raw=true) + +#### Scrolling Animation + +![Modal sheet elements in use](https://github.com/woltapp/wolt_modal_sheet/blob/main/doc/scrolling_modal_sheet.png?raw=true) + +### Example Configuration + +```dart +WoltModalSheetThemeData( + animationStyle: WoltModalSheetAnimationStyle( + paginationAnimationStyle: WoltModalSheetPaginationAnimationStyle( + mainContentIncomingOpacityCurve: const Interval( + 150 / 350, + 350 / 350, + curve: Curves.linear, + ), + modalSheetHeightTransitionCurve: const Interval( + 0 / 350, + 300 / 350, + curve: Curves.fastOutSlowIn, + ), + incomingSabOpacityCurve: const Interval( + 100 / 350, + 300 / 350, + curve: Curves.linear, + ), + // Define additional pagination animation styles as needed. + ), + scrollAnimationStyle: WoltModalSheetScrollAnimationStyle( + heroImageScaleStart: 1.0, + heroImageScaleEnd: 0.9, + topBarTitleTranslationYInPixels: 8.0, + topBarTranslationYInPixels: 4.0, + // Define additional scroll animation styles as needed. + ), + ), +), +``` + ## Usage of WoltModalSheet Pages The WoltModalSheet library provides three primary classes for constructing diff --git a/doc/pagination_modal_sheet.png b/doc/pagination_modal_sheet.png new file mode 100644 index 00000000..c7fcff64 Binary files /dev/null and b/doc/pagination_modal_sheet.png differ diff --git a/doc/scrolling_modal_sheet.png b/doc/scrolling_modal_sheet.png new file mode 100644 index 00000000..7b99326f Binary files /dev/null and b/doc/scrolling_modal_sheet.png differ diff --git a/lib/src/content/components/main_content/wolt_modal_sheet_hero_image.dart b/lib/src/content/components/main_content/wolt_modal_sheet_hero_image.dart index 1e8bc38f..e09a659e 100644 --- a/lib/src/content/components/main_content/wolt_modal_sheet_hero_image.dart +++ b/lib/src/content/components/main_content/wolt_modal_sheet_hero_image.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:wolt_modal_sheet/src/utils/wolt_layout_transformation_utils.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; /// The hero image widget displayed on top of the main content. /// @@ -19,6 +20,7 @@ class WoltModalSheetHeroImage extends StatelessWidget { required this.heroImage, required this.topBarHeight, required this.heroImageHeight, + required this.scrollAnimationStyle, Key? key, }) : super(key: key); @@ -31,6 +33,9 @@ class WoltModalSheetHeroImage extends StatelessWidget { /// The height of the hero image. final double heroImageHeight; + /// Animation styles for scrolling within Wolt Modal Sheet Page. + final WoltModalSheetScrollAnimationStyle scrollAnimationStyle; + @override Widget build(BuildContext context) { return Flow( @@ -38,6 +43,7 @@ class WoltModalSheetHeroImage extends StatelessWidget { scrollPosition: Scrollable.of(context).position, topBarHeight: topBarHeight, heroImageHeight: heroImageHeight, + scrollAnimationStyle: scrollAnimationStyle, ), children: [heroImage], ); @@ -57,6 +63,7 @@ class _HeroImageFlowDelegate extends FlowDelegate { required this.scrollPosition, required this.topBarHeight, required this.heroImageHeight, + required this.scrollAnimationStyle, }) : super(repaint: scrollPosition); /// The scroll position of the modal sheet. @@ -68,6 +75,9 @@ class _HeroImageFlowDelegate extends FlowDelegate { /// The height of the hero image. final double heroImageHeight; + /// Animation styles for scrolling within Wolt Modal Sheet Page. + final WoltModalSheetScrollAnimationStyle scrollAnimationStyle; + @override Size getSize(BoxConstraints constraints) { return super.getSize(constraints.copyWith(maxHeight: heroImageHeight)); @@ -88,8 +98,8 @@ class _HeroImageFlowDelegate extends FlowDelegate { // Calculate scale final double scale = WoltLayoutTransformationUtils.calculateTransformationValue( - startValue: 1.1, - endValue: 1.0, + startValue: scrollAnimationStyle.heroImageScaleStart, + endValue: scrollAnimationStyle.heroImageScaleEnd, rangeInPx: heroImageHeight - topBarHeight - 8, progressInRangeInPx: currentScrollPosition, ); @@ -100,8 +110,8 @@ class _HeroImageFlowDelegate extends FlowDelegate { rangeInPx: (((heroImageHeight - topBarHeight) / 2) - 8), progressInRangeInPx: currentScrollPosition - ((heroImageHeight - topBarHeight) / 2), - startValue: 1.0, - endValue: 0.0, + startValue: scrollAnimationStyle.heroImageOpacityStart, + endValue: scrollAnimationStyle.heroImageOpacityEnd, ); // Calculate the translation to center the image diff --git a/lib/src/content/components/main_content/wolt_modal_sheet_main_content.dart b/lib/src/content/components/main_content/wolt_modal_sheet_main_content.dart index 13c4030b..b27ae86f 100644 --- a/lib/src/content/components/main_content/wolt_modal_sheet_main_content.dart +++ b/lib/src/content/components/main_content/wolt_modal_sheet_main_content.dart @@ -12,12 +12,14 @@ class WoltModalSheetMainContent extends StatelessWidget { final GlobalKey pageTitleKey; final SliverWoltModalSheetPage page; final WoltModalType woltModalType; + final WoltModalSheetScrollAnimationStyle scrollAnimationStyle; const WoltModalSheetMainContent({ required this.scrollController, required this.pageTitleKey, required this.page, required this.woltModalType, + required this.scrollAnimationStyle, Key? key, }) : super(key: key); @@ -61,6 +63,7 @@ class WoltModalSheetMainContent extends StatelessWidget { topBarHeight: topBarHeight, heroImage: heroImage, heroImageHeight: heroImageHeight, + scrollAnimationStyle: scrollAnimationStyle, ) // If top bar layer is always visible, the padding is explicitly added to the // scroll view since top bar will not be integrated to scroll view at all. diff --git a/lib/src/content/components/main_content/wolt_modal_sheet_top_bar_flow.dart b/lib/src/content/components/main_content/wolt_modal_sheet_top_bar_flow.dart index 44ba97ec..662faeef 100644 --- a/lib/src/content/components/main_content/wolt_modal_sheet_top_bar_flow.dart +++ b/lib/src/content/components/main_content/wolt_modal_sheet_top_bar_flow.dart @@ -16,16 +16,16 @@ import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; class WoltModalSheetTopBarFlow extends StatelessWidget { final ScrollController scrollController; final GlobalKey titleKey; - final double topBarTranslationYAmountInPx; final SliverWoltModalSheetPage page; final ValueListenable softKeyboardClosedListenable; + final WoltModalSheetScrollAnimationStyle scrollAnimationStyle; const WoltModalSheetTopBarFlow({ required this.page, required this.scrollController, required this.titleKey, - required this.topBarTranslationYAmountInPx, required this.softKeyboardClosedListenable, + required this.scrollAnimationStyle, Key? key, }) : super(key: key); @@ -48,8 +48,8 @@ class WoltModalSheetTopBarFlow extends StatelessWidget { heroImageHeight: heroImageHeight, scrollController: scrollController, titleKey: titleKey, - topBarTranslationYAmountInPx: topBarTranslationYAmountInPx, softKeyboardClosedListenable: softKeyboardClosedListenable, + scrollAnimationStyle: scrollAnimationStyle, ), children: [WoltModalSheetTopBar(page: page)], ); @@ -61,15 +61,15 @@ class _TopBarFlowDelegate extends FlowDelegate { final double heroImageHeight; final ScrollController scrollController; final GlobalKey titleKey; - final double topBarTranslationYAmountInPx; final ValueListenable softKeyboardClosedListenable; + final WoltModalSheetScrollAnimationStyle scrollAnimationStyle; _TopBarFlowDelegate({ required this.topBarHeight, required this.heroImageHeight, required this.scrollController, required this.titleKey, - required this.topBarTranslationYAmountInPx, + required this.scrollAnimationStyle, required this.softKeyboardClosedListenable, }) : super( repaint: Listenable.merge([ @@ -84,7 +84,8 @@ class _TopBarFlowDelegate extends FlowDelegate { void paintChildren(FlowPaintingContext context) { final pageTitleHeight = titleKey.currentContext!.size!.height; - final topBarTranslationYStart = -1 * topBarTranslationYAmountInPx; + final topBarTranslationYStart = + -1 * scrollAnimationStyle.topBarTranslationYInPixels; const topBarTranslationYEnd = 0.0; final topBarTranslationYAndOpacityStartPoint = heroImageHeight == 0 ? 0 : heroImageHeight - topBarHeight - 8; @@ -105,8 +106,8 @@ class _TopBarFlowDelegate extends FlowDelegate { rangeInPx: 8, progressInRangeInPx: currentScrollOffset - topBarTranslationYAndOpacityStartPoint, - startValue: 0.0, - endValue: 1.0, + startValue: scrollAnimationStyle.topBarOpacityStart, + endValue: scrollAnimationStyle.topBarOpacityEnd, ); /// Paint Top Bar @@ -122,8 +123,6 @@ class _TopBarFlowDelegate extends FlowDelegate { return heroImageHeight != oldDelegate.heroImageHeight || titleKey != oldDelegate.titleKey || currentScrollOffset != oldDelegate.currentScrollOffset || - topBarTranslationYAmountInPx != - oldDelegate.topBarTranslationYAmountInPx || softKeyboardClosedListenable.value != oldDelegate.softKeyboardClosedListenable.value || topBarHeight != oldDelegate.topBarHeight; diff --git a/lib/src/content/components/main_content/wolt_modal_sheet_top_bar_title_flow.dart b/lib/src/content/components/main_content/wolt_modal_sheet_top_bar_title_flow.dart index 637c6b88..b2dde311 100644 --- a/lib/src/content/components/main_content/wolt_modal_sheet_top_bar_title_flow.dart +++ b/lib/src/content/components/main_content/wolt_modal_sheet_top_bar_title_flow.dart @@ -18,6 +18,7 @@ class WoltModalSheetTopBarTitleFlow extends StatelessWidget { final SliverWoltModalSheetPage page; final Widget topBarTitle; final ValueListenable softKeyboardClosedListenable; + final WoltModalSheetScrollAnimationStyle scrollAnimationStyle; const WoltModalSheetTopBarTitleFlow({ required this.page, @@ -25,11 +26,10 @@ class WoltModalSheetTopBarTitleFlow extends StatelessWidget { required this.titleKey, required this.topBarTitle, required this.softKeyboardClosedListenable, + required this.scrollAnimationStyle, Key? key, }) : super(key: key); - static const _topBarTitleTranslationYAmount = 8.0; - @override Widget build(BuildContext context) { final themeData = Theme.of(context).extension(); @@ -49,6 +49,7 @@ class WoltModalSheetTopBarTitleFlow extends StatelessWidget { scrollController: scrollController, titleKey: titleKey, softKeyboardClosedNotifier: softKeyboardClosedListenable, + scrollAnimationStyle: scrollAnimationStyle, ), children: [Center(child: topBarTitle)], ); @@ -61,6 +62,7 @@ class _TopBarTitleFlowDelegate extends FlowDelegate { final ScrollController scrollController; final GlobalKey titleKey; final ValueListenable softKeyboardClosedNotifier; + final WoltModalSheetScrollAnimationStyle scrollAnimationStyle; _TopBarTitleFlowDelegate({ required this.topBarHeight, @@ -68,6 +70,7 @@ class _TopBarTitleFlowDelegate extends FlowDelegate { required this.scrollController, required this.titleKey, required this.softKeyboardClosedNotifier, + required this.scrollAnimationStyle, }) : super( repaint: Listenable.merge([ scrollController, @@ -80,11 +83,10 @@ class _TopBarTitleFlowDelegate extends FlowDelegate { @override void paintChildren(FlowPaintingContext context) { final double pageTitleHeight = titleKey.currentContext!.size!.height; - const topBarTitleTranslationYStart = - -1 * WoltModalSheetTopBarTitleFlow._topBarTitleTranslationYAmount; - const topBarTitleTranslationYAmount = - WoltModalSheetTopBarTitleFlow._topBarTitleTranslationYAmount; - const topBarTitleTranslationYEnd = + final topBarTitleTranslationYAmount = + scrollAnimationStyle.topBarTitleTranslationYInPixels; + final topBarTitleTranslationYStart = -1 * topBarTitleTranslationYAmount; + final topBarTitleTranslationYEnd = topBarTitleTranslationYStart + topBarTitleTranslationYAmount; final topBarTitleTranslationYAndOpacityStartPoint = @@ -106,8 +108,8 @@ class _TopBarTitleFlowDelegate extends FlowDelegate { rangeInPx: pageTitleHeight / 2, progressInRangeInPx: currentScrollPosition - topBarTitleTranslationYAndOpacityStartPoint, - startValue: 0.0, - endValue: 1.0, + startValue: scrollAnimationStyle.topBarTitleOpacityStart, + endValue: scrollAnimationStyle.topBarTitleOpacityEnd, ); /// Paint Top Bar Title diff --git a/lib/src/content/components/paginating_group/main_content_animated_builder.dart b/lib/src/content/components/paginating_group/main_content_animated_builder.dart index 48ad69f2..9f6c2ffe 100644 --- a/lib/src/content/components/paginating_group/main_content_animated_builder.dart +++ b/lib/src/content/components/paginating_group/main_content_animated_builder.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:wolt_modal_sheet/src/content/components/paginating_group/wolt_modal_sheet_page_transition_state.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; class MainContentAnimatedBuilder extends StatefulWidget { final AnimationController controller; @@ -9,6 +10,7 @@ class MainContentAnimatedBuilder extends StatefulWidget { final bool forwardMove; final double sheetWidth; final WoltModalSheetPageTransitionState pageTransitionState; + final WoltModalSheetPaginationAnimationStyle paginationAnimationStyle; const MainContentAnimatedBuilder({ required this.controller, @@ -18,6 +20,7 @@ class MainContentAnimatedBuilder extends StatefulWidget { required this.forwardMove, required this.sheetWidth, required this.pageTransitionState, + required this.paginationAnimationStyle, super.key, }); @@ -41,8 +44,9 @@ class _MainContentAnimatedBuilderState if (_sizeFactor == null && incomingContext?.mounted == true && outgoingContext?.mounted == true) { - _sizeFactor = widget.pageTransitionState.mainContentSizeFactor( + _sizeFactor = widget.pageTransitionState.mainContentHeightTransition( widget.controller, + widget.paginationAnimationStyle, incomingMainContentHeight: incomingContext!.size!.height, outgoingMainContentHeight: outgoingContext!.size!.height, ); @@ -63,10 +67,13 @@ class _MainContentAnimatedBuilderState sizeFactor: _sizeFactor ?? pageTransitionState.defaultMainContentSizeFactor(controller), child: Opacity( - opacity: pageTransitionState.mainContentOpacity(controller).value, + opacity: pageTransitionState + .mainContentOpacity(controller, widget.paginationAnimationStyle) + .value, child: SlideTransition( position: pageTransitionState.mainContentSlidePosition( controller, + widget.paginationAnimationStyle, sheetWidth: widget.sheetWidth, screenWidth: screenWidth, isForwardMove: widget.forwardMove, diff --git a/lib/src/content/components/paginating_group/navigation_toolbar_animated_builder.dart b/lib/src/content/components/paginating_group/navigation_toolbar_animated_builder.dart index 791a5942..5b8339b7 100644 --- a/lib/src/content/components/paginating_group/navigation_toolbar_animated_builder.dart +++ b/lib/src/content/components/paginating_group/navigation_toolbar_animated_builder.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; import 'wolt_modal_sheet_page_transition_state.dart'; @@ -6,17 +7,22 @@ class NavigationToolbarAnimatedBuilder extends StatelessWidget { final AnimationController controller; final Widget child; final WoltModalSheetPageTransitionState pageTransitionState; + final WoltModalSheetPaginationAnimationStyle paginationAnimationStyle; const NavigationToolbarAnimatedBuilder({ required this.pageTransitionState, required this.controller, required this.child, + required this.paginationAnimationStyle, super.key, }); @override Widget build(BuildContext context) { - final opacity = pageTransitionState.navigationToolbarOpacity(controller); + final opacity = pageTransitionState.navigationToolbarOpacity( + controller, + paginationAnimationStyle, + ); return AnimatedBuilder( animation: controller, builder: (BuildContext _, __) { diff --git a/lib/src/content/components/paginating_group/sab_animated_builder.dart b/lib/src/content/components/paginating_group/sab_animated_builder.dart index 0a8ac023..33d656e9 100644 --- a/lib/src/content/components/paginating_group/sab_animated_builder.dart +++ b/lib/src/content/components/paginating_group/sab_animated_builder.dart @@ -1,21 +1,25 @@ import 'package:flutter/material.dart'; import 'package:wolt_modal_sheet/src/content/components/paginating_group/wolt_modal_sheet_page_transition_state.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; class SabAnimatedBuilder extends StatelessWidget { final AnimationController controller; final WoltModalSheetPageTransitionState pageTransitionState; final Widget child; + final WoltModalSheetPaginationAnimationStyle paginationAnimationStyle; const SabAnimatedBuilder({ required this.controller, required this.child, required this.pageTransitionState, + required this.paginationAnimationStyle, super.key, }); @override Widget build(BuildContext context) { - final opacity = pageTransitionState.sabOpacity(controller); + final opacity = + pageTransitionState.sabOpacity(controller, paginationAnimationStyle); return AnimatedBuilder( animation: controller, builder: (BuildContext _, __) { diff --git a/lib/src/content/components/paginating_group/top_bar_animated_builder.dart b/lib/src/content/components/paginating_group/top_bar_animated_builder.dart index c50470fe..7009f6fb 100644 --- a/lib/src/content/components/paginating_group/top_bar_animated_builder.dart +++ b/lib/src/content/components/paginating_group/top_bar_animated_builder.dart @@ -1,21 +1,25 @@ import 'package:flutter/material.dart'; import 'package:wolt_modal_sheet/src/content/components/paginating_group/wolt_modal_sheet_page_transition_state.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; class TopBarAnimatedBuilder extends StatelessWidget { final AnimationController controller; final WoltModalSheetPageTransitionState pageTransitionState; final Widget child; + final WoltModalSheetPaginationAnimationStyle paginationAnimationStyle; const TopBarAnimatedBuilder({ required this.controller, required this.child, required this.pageTransitionState, + required this.paginationAnimationStyle, super.key, }); @override Widget build(BuildContext context) { - final opacity = pageTransitionState.sabOpacity(controller); + final opacity = + pageTransitionState.sabOpacity(controller, paginationAnimationStyle); return AnimatedBuilder( animation: controller, builder: (BuildContext _, __) { diff --git a/lib/src/content/components/paginating_group/wolt_modal_sheet_page_transition_state.dart b/lib/src/content/components/paginating_group/wolt_modal_sheet_page_transition_state.dart index 7cf84d59..758032f6 100644 --- a/lib/src/content/components/paginating_group/wolt_modal_sheet_page_transition_state.dart +++ b/lib/src/content/components/paginating_group/wolt_modal_sheet_page_transition_state.dart @@ -1,4 +1,5 @@ import 'package:flutter/widgets.dart'; +import 'package:wolt_modal_sheet/wolt_modal_sheet.dart'; enum WoltModalSheetPageTransitionState { incoming, @@ -16,120 +17,146 @@ enum WoltModalSheetPageTransitionState { } } - Animation mainContentSizeFactor( - AnimationController controller, { + Animation mainContentHeightTransition( + AnimationController controller, + WoltModalSheetPaginationAnimationStyle style, { required double incomingMainContentHeight, required double outgoingMainContentHeight, }) { - const interval = Interval(0 / 350, 300 / 350, curve: Curves.fastOutSlowIn); + final interval = style.modalSheetHeightTransitionCurve; switch (this) { case WoltModalSheetPageTransitionState.incoming: return Tween( - begin: outgoingMainContentHeight / incomingMainContentHeight, - end: 1.0) - .animate(CurvedAnimation(parent: controller, curve: interval)); + begin: outgoingMainContentHeight / incomingMainContentHeight, + end: 1.0, + ).animate(CurvedAnimation(parent: controller, curve: interval)); case WoltModalSheetPageTransitionState.outgoing: return Tween( - begin: 1.0, - end: incomingMainContentHeight / outgoingMainContentHeight) - .animate(CurvedAnimation(parent: controller, curve: interval)); + begin: 1.0, + end: incomingMainContentHeight / outgoingMainContentHeight, + ).animate(CurvedAnimation(parent: controller, curve: interval)); } } - Animation mainContentOpacity(AnimationController controller) { + Animation mainContentOpacity( + AnimationController controller, + WoltModalSheetPaginationAnimationStyle style, + ) { switch (this) { case WoltModalSheetPageTransitionState.incoming: return Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: controller, - curve: const Interval(150 / 350, 350 / 350, curve: Curves.linear), + curve: style.mainContentIncomingOpacityCurve, ), ); case WoltModalSheetPageTransitionState.outgoing: return Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation( parent: controller, - curve: const Interval(50 / 350, 150 / 350, curve: Curves.linear), + curve: style.mainContentOutgoingOpacityCurve, ), ); } } Animation mainContentSlidePosition( - AnimationController controller, { + AnimationController controller, + WoltModalSheetPaginationAnimationStyle style, { required double sheetWidth, required double screenWidth, required bool isForwardMove, }) { - const interval = Interval(50 / 350, 350 / 350, curve: Curves.fastOutSlowIn); switch (this) { case WoltModalSheetPageTransitionState.incoming: + final incomingBeginOffset = Offset( + sheetWidth * 0.3 * (isForwardMove ? 1 : -1) / screenWidth, 0); return Tween( - begin: Offset( - sheetWidth * 0.3 * (isForwardMove ? 1 : -1) / screenWidth, 0), - end: Offset.zero, - ).animate(CurvedAnimation(parent: controller, curve: interval)); + begin: + style.incomingMainContentSlideBeginOffset ?? incomingBeginOffset, + end: style.incomingMainContentSlideEndOffset, + ).animate( + CurvedAnimation( + parent: controller, + curve: style.mainContentIncomingSlidePositionCurve, + ), + ); case WoltModalSheetPageTransitionState.outgoing: + final outgoingEndOffset = Offset( + sheetWidth * 0.3 * (isForwardMove ? -1 : 1) / screenWidth, 0); return Tween( - begin: Offset.zero, - end: Offset( - sheetWidth * 0.3 * (isForwardMove ? -1 : 1) / screenWidth, 0), - ).animate(CurvedAnimation(parent: controller, curve: interval)); + begin: style.outgoingMainContentSlideBeginOffset, + end: style.outgoingMainContentSlideEndOffset ?? outgoingEndOffset, + ).animate( + CurvedAnimation( + parent: controller, + curve: style.mainContentIncomingSlidePositionCurve, + ), + ); } } - Animation navigationToolbarOpacity(AnimationController controller) { + Animation navigationToolbarOpacity( + AnimationController controller, + WoltModalSheetPaginationAnimationStyle style, + ) { switch (this) { case WoltModalSheetPageTransitionState.incoming: return Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: controller, - curve: const Interval(100 / 350, 300 / 350, curve: Curves.linear), + curve: style.incomingNavigationToolbarOpacityCurve, ), ); case WoltModalSheetPageTransitionState.outgoing: return Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation( parent: controller, - curve: const Interval(0, 100 / 350, curve: Curves.linear), + curve: style.incomingNavigationToolbarOpacityCurve, ), ); } } - Animation sabOpacity(AnimationController controller) { + Animation sabOpacity( + AnimationController controller, + WoltModalSheetPaginationAnimationStyle style, + ) { switch (this) { case WoltModalSheetPageTransitionState.incoming: return Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: controller, - curve: const Interval(100 / 350, 300 / 350, curve: Curves.linear), + curve: style.incomingSabOpacityCurve, ), ); case WoltModalSheetPageTransitionState.outgoing: return Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation( parent: controller, - curve: const Interval(0, 100 / 350), + curve: style.outgoingSabOpacityCurve, ), ); } } - Animation topBarOpacity(AnimationController controller) { + Animation topBarOpacity( + AnimationController controller, + WoltModalSheetPaginationAnimationStyle style, + ) { switch (this) { case WoltModalSheetPageTransitionState.incoming: return Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: controller, - curve: const Interval(150 / 350, 350 / 350, curve: Curves.linear), + curve: style.incomingTopBarOpacityCurve, ), ); case WoltModalSheetPageTransitionState.outgoing: return Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation( parent: controller, - curve: const Interval(0, 150 / 350, curve: Curves.linear), + curve: style.outgoingTopBarOpacityCurve, ), ); } diff --git a/lib/src/content/wolt_modal_sheet_animated_switcher.dart b/lib/src/content/wolt_modal_sheet_animated_switcher.dart index 54e54bb1..258628e6 100644 --- a/lib/src/content/wolt_modal_sheet_animated_switcher.dart +++ b/lib/src/content/wolt_modal_sheet_animated_switcher.dart @@ -58,8 +58,6 @@ class _WoltModalSheetAnimatedSwitcherState defaultThemeData.hasTopBarLayer; } - double get _topBarTranslationY => _hasTopBarLayer ? 4 : 0; - late List _titleKeys; late List _mainContentKeys; late List _offstagedTitleKeys; @@ -202,6 +200,7 @@ class _WoltModalSheetAnimatedSwitcherState final outgoingWidgets = _outgoingPageWidgets; final animationController = _animationController; final animatePagination = _shouldAnimatePagination; + return Stack( alignment: Alignment.bottomCenter, children: [ @@ -210,7 +209,6 @@ class _WoltModalSheetAnimatedSwitcherState paginatingWidgetsGroup: outgoingWidgets, page: _page, woltModalType: widget.woltModalType, - topBarTranslationY: _topBarTranslationY, showDragHandle: widget.showDragHandle, ), if (incomingWidgets != null) @@ -218,7 +216,6 @@ class _WoltModalSheetAnimatedSwitcherState paginatingWidgetsGroup: incomingWidgets, page: _page, woltModalType: widget.woltModalType, - topBarTranslationY: _topBarTranslationY, showDragHandle: widget.showDragHandle, ), if (incomingWidgets != null && @@ -327,6 +324,8 @@ class _WoltModalSheetAnimatedSwitcherState final navBarHeight = _page.navBarHeight ?? themeData?.navBarHeight ?? defaultThemeData.navBarHeight; + final WoltModalSheetAnimationStyle animationStyle = + themeData?.animationStyle ?? defaultThemeData.animationStyle; const animatedBuilderKey = ValueKey(WoltModalSheetPageTransitionState.incoming); // If the page uses the default top bar, we should show the top bar title to be represented in @@ -338,6 +337,7 @@ class _WoltModalSheetAnimatedSwitcherState isTopBarLayerAlwaysVisible || _page is NonScrollingWoltModalSheetPage ? Center(child: topBarTitle) : WoltModalSheetTopBarTitleFlow( + scrollAnimationStyle: animationStyle.scrollAnimationStyle, page: _page, scrollController: _currentPageScrollController, titleKey: _pageTitleKey, @@ -348,6 +348,7 @@ class _WoltModalSheetAnimatedSwitcherState return PaginatingWidgetsGroup( mainContentAnimatedBuilder: MainContentAnimatedBuilder( key: animatedBuilderKey, + paginationAnimationStyle: animationStyle.paginationAnimationStyle, pageTransitionState: WoltModalSheetPageTransitionState.incoming, controller: animationController, incomingOffstagedMainContentKey: @@ -360,12 +361,16 @@ class _WoltModalSheetAnimatedSwitcherState mainContentKey: _mainContentKeys[_pageIndex], titleKey: _pageTitleKey, scrollController: _currentPageScrollController, + scrollAnimationStyle: animationStyle.scrollAnimationStyle, ), ), - offstagedMainContent: - _createMainContent(titleKey: _offstagedTitleKeys[_pageIndex]), + offstagedMainContent: _createMainContent( + titleKey: _offstagedTitleKeys[_pageIndex], + scrollAnimationStyle: animationStyle.scrollAnimationStyle, + ), topBarAnimatedBuilder: TopBarAnimatedBuilder( key: animatedBuilderKey, + paginationAnimationStyle: animationStyle.paginationAnimationStyle, pageTransitionState: WoltModalSheetPageTransitionState.incoming, controller: animationController, child: hasTopBarLayer @@ -373,9 +378,9 @@ class _WoltModalSheetAnimatedSwitcherState _page is NonScrollingWoltModalSheetPage ? WoltModalSheetTopBar(page: _page) : WoltModalSheetTopBarFlow( + scrollAnimationStyle: animationStyle.scrollAnimationStyle, page: _page, scrollController: _currentPageScrollController, - topBarTranslationYAmountInPx: _topBarTranslationY, titleKey: _pageTitleKey, softKeyboardClosedListenable: _softKeyboardClosedNotifier, )) @@ -383,6 +388,7 @@ class _WoltModalSheetAnimatedSwitcherState ), navigationToolbarAnimatedBuilder: NavigationToolbarAnimatedBuilder( key: animatedBuilderKey, + paginationAnimationStyle: animationStyle.paginationAnimationStyle, pageTransitionState: WoltModalSheetPageTransitionState.incoming, controller: animationController, child: SizedBox( @@ -397,6 +403,7 @@ class _WoltModalSheetAnimatedSwitcherState ), sabAnimatedBuilder: SabAnimatedBuilder( key: animatedBuilderKey, + paginationAnimationStyle: animationStyle.paginationAnimationStyle, pageTransitionState: WoltModalSheetPageTransitionState.incoming, controller: animationController, child: WoltStickyActionBarWrapper(page: _page), @@ -410,9 +417,14 @@ class _WoltModalSheetAnimatedSwitcherState ) { const animatedBuilderKey = ValueKey(WoltModalSheetPageTransitionState.outgoing); + final themeData = Theme.of(context).extension(); + final defaultThemeData = WoltModalSheetDefaultThemeData(context); + final WoltModalSheetAnimationStyle animationStyle = + themeData?.animationStyle ?? defaultThemeData.animationStyle; return PaginatingWidgetsGroup( mainContentAnimatedBuilder: MainContentAnimatedBuilder( key: animatedBuilderKey, + paginationAnimationStyle: animationStyle.paginationAnimationStyle, pageTransitionState: WoltModalSheetPageTransitionState.outgoing, controller: animationController, incomingOffstagedMainContentKey: @@ -430,12 +442,14 @@ class _WoltModalSheetAnimatedSwitcherState ), topBarAnimatedBuilder: TopBarAnimatedBuilder( key: animatedBuilderKey, + paginationAnimationStyle: animationStyle.paginationAnimationStyle, pageTransitionState: WoltModalSheetPageTransitionState.outgoing, controller: animationController, child: currentWidgetsToBeOutgoing.topBarAnimatedBuilder.child, ), navigationToolbarAnimatedBuilder: NavigationToolbarAnimatedBuilder( key: animatedBuilderKey, + paginationAnimationStyle: animationStyle.paginationAnimationStyle, pageTransitionState: WoltModalSheetPageTransitionState.outgoing, controller: animationController, child: @@ -443,6 +457,7 @@ class _WoltModalSheetAnimatedSwitcherState ), sabAnimatedBuilder: SabAnimatedBuilder( key: animatedBuilderKey, + paginationAnimationStyle: animationStyle.paginationAnimationStyle, pageTransitionState: WoltModalSheetPageTransitionState.outgoing, controller: animationController, child: currentWidgetsToBeOutgoing.sabAnimatedBuilder.child, @@ -452,11 +467,13 @@ class _WoltModalSheetAnimatedSwitcherState WoltModalSheetMainContent _createMainContent({ required GlobalKey titleKey, + required WoltModalSheetScrollAnimationStyle scrollAnimationStyle, GlobalKey? mainContentKey, ScrollController? scrollController, }) { return WoltModalSheetMainContent( key: mainContentKey, + scrollAnimationStyle: scrollAnimationStyle, pageTitleKey: titleKey, scrollController: scrollController, page: _page, diff --git a/lib/src/content/wolt_modal_sheet_layout.dart b/lib/src/content/wolt_modal_sheet_layout.dart index a33561ae..77e1b904 100644 --- a/lib/src/content/wolt_modal_sheet_layout.dart +++ b/lib/src/content/wolt_modal_sheet_layout.dart @@ -10,7 +10,6 @@ class WoltModalSheetLayout extends StatelessWidget { required this.page, required this.paginatingWidgetsGroup, required this.woltModalType, - required this.topBarTranslationY, required this.showDragHandle, Key? key, }) : super(key: key); @@ -18,7 +17,6 @@ class WoltModalSheetLayout extends StatelessWidget { final SliverWoltModalSheetPage page; final PaginatingWidgetsGroup paginatingWidgetsGroup; final WoltModalType woltModalType; - final double topBarTranslationY; final bool showDragHandle; @override diff --git a/lib/src/theme/wolt_modal_sheet_animation_style.dart b/lib/src/theme/wolt_modal_sheet_animation_style.dart new file mode 100644 index 00000000..951d4dff --- /dev/null +++ b/lib/src/theme/wolt_modal_sheet_animation_style.dart @@ -0,0 +1,265 @@ +import 'package:flutter/material.dart'; + +/// Motion animation styles for both pagination and scrolling within a Wolt Modal Sheet. +/// +/// This class serves as a configuration for the motion animations used within Wolt Modal Sheet. +/// It combines both pagination and scroll-related animations, allowing for a unified approach to +/// defining the visual transitions that occur during user interactions with the modal sheet. +/// +/// Properties: +/// - `paginationAnimationStyle`: Defines the animation styles for transitions between pages +/// within the modal sheet. This includes animations for the main content's height, slide +/// positions, and opacity transitions for components like the navigation toolbar and top bar. It +/// allows for fine-tuned control over how these transitions appear and feel to the user, +/// enhancing the pagination experience. +/// - `scrollAnimationStyle`: Specifies the animation styles for scroll interactions within the +/// modal sheet. This can include dynamic effects such as scaling or fading of images, adjustment +/// of top bar visibility, and other scroll-driven animations. These settings allow for a more +/// interactive and engaging user experience as content is explored by scrolling within the modal +/// sheet. +/// +/// Example: +/// ``` +/// WoltModalSheetAnimationStyle( +/// paginationAnimationStyle: WoltModalSheetPaginationAnimationStyle( +/// // Custom pagination animation configurations +/// ), +/// scrollAnimationStyle: WoltModalSheetScrollAnimationStyle( +/// // Custom scroll animation configurations +/// ), +/// ) +/// ``` +class WoltModalSheetAnimationStyle { + final WoltModalSheetPaginationAnimationStyle paginationAnimationStyle; + final WoltModalSheetScrollAnimationStyle scrollAnimationStyle; + + const WoltModalSheetAnimationStyle({ + this.paginationAnimationStyle = + const WoltModalSheetPaginationAnimationStyle(), + this.scrollAnimationStyle = const WoltModalSheetScrollAnimationStyle(), + }); +} + +/// Defines the animation styles for scrolling within Wolt Modal Sheet when the top bar component +/// of the modal sheet is set to enable becoming visible as the user scrolls. +/// +/// Defines the customizable animation parameters for the hero image, top bar, and top bar title +/// within the modal sheet. These parameters control how these elements transform and fade in +/// response to scroll events as the top bar component of the modal sheet becomes visible. +class WoltModalSheetScrollAnimationStyle { + /// The starting scale of the hero image when the modal sheet is at its initial + /// scroll position. Default is 1.0, meaning no scaling. + final double heroImageScaleStart; + + /// The ending scale of the hero image when the modal sheet has been scrolled + /// to a certain point. Default value of 1.0 means the image will remain at its + /// original size. + final double heroImageScaleEnd; + + /// The starting opacity of the hero image. Default is 1.0, fully opaque. + final double heroImageOpacityStart; + + /// The ending opacity of the hero image as the modal sheet is scrolled. Default is 0.0, + ///which means fully transparent. + final double heroImageOpacityEnd; + + /// The total vertical translation (in pixels) of the top bar. + final double topBarTranslationYInPixels; + + /// The starting opacity of the top bar, with 0.0 being fully transparent + /// and typically used to initially hide the bar before scrolling starts and it fades in. + final double topBarOpacityStart; + + /// The ending opacity of the top bar, where 1.0 means fully opaque and + /// typically used to fully reveal the bar as it is scrolled into view. + final double topBarOpacityEnd; + + /// The total vertical translation (in pixels) of the top bar title. + final double topBarTitleTranslationYInPixels; + + /// The starting opacity of the top bar title, with 0.0 being fully transparent. + /// This allows the title to fade in from a transparent state as the modal sheet + /// is scrolled. + final double topBarTitleOpacityStart; + + /// The ending opacity of the top bar title, with 1.0 being fully opaque. + /// This is typically used to ensure the title is fully visible once the top bar + /// has been scrolled into its final position. + final double topBarTitleOpacityEnd; + + /// Constructs a [WoltModalSheetScrollAnimationStyle] with customizable animation + /// parameters for the hero image, top bar, and top bar title as the top bar becomes visible + /// according to the scroll changes. Default values are provided, but can be overridden to + /// achieve custom animation effects. + /// + /// Parameters allow for the specification of start/end scales and opacities for + /// the hero image, and y translations and opacities for both the top bar + /// and the top bar title. + const WoltModalSheetScrollAnimationStyle({ + this.heroImageScaleStart = 1.0, + this.heroImageScaleEnd = 1.0, + this.heroImageOpacityStart = 1.0, + this.heroImageOpacityEnd = 0.0, + this.topBarTranslationYInPixels = 4.0, + this.topBarOpacityStart = 0, + this.topBarOpacityEnd = 1, + this.topBarTitleTranslationYInPixels = 8.0, + this.topBarTitleOpacityStart = 0, + this.topBarTitleOpacityEnd = 1, + }); +} + +/// Defines the animation styles for pagination transitions within Wolt Modal Sheet. +/// +/// This class provides customization for animations triggered during page changes in a +/// multi-page modal sheet. It allows detailed customization of animation aspects for modal sheet +/// height adjustments, slide positions of the main content, and opacity transitions for various +/// components such as the navigation toolbar, sticky action button (SAB), and top bar. By +/// utilizing customizable animation curves, users can precisely control the timing and behavior of +/// these animations to customize transitions between pages within the modal sheet. +/// +/// Animations defined in this class are triggered during pagination, which occurs when the modal +/// sheet changes from one page to another. These animations include: +/// - Modal sheet height transitions: Adjusts the height of the modal sheet to fit the content of +/// the incoming page so that during the transition both incoming and outgoing pages have the +/// same height. +/// - Main content slide transitions: Moves the main content horizontally or vertically, creating +/// a seamless transition effect during the transition. +/// - Opacity transitions for components: Fades in or out specific components (navigation +/// toolbar, SAB, top bar). +/// +/// By adjusting the provided animation curves, developers can create custom animations that +/// align with the overall design language of their application. + +class WoltModalSheetPaginationAnimationStyle { + /// Curve for animating the height transition of the modal sheet height during pagination. The + /// height of the modal sheet is always equal to the height of the main content, and this curve + /// controls how the height of the main content's of both the incoming and outgoing pages + /// changes. During the transition, the height of the both incoming and outgoing pages are equal. + final Curve modalSheetHeightTransitionCurve; + + /// Curve for the slide position animation of the main content when it is incoming. + /// Defines the motion trajectory for entering content. + final Curve mainContentIncomingSlidePositionCurve; + + /// Curve for the slide position animation of the main content when it is outgoing. + /// Defines the motion trajectory for exiting content. + final Curve mainContentOutgoingSlidePositionCurve; + + /// Curve for the opacity animation of the main content when incoming. + /// Controls how the opacity of entering main content changes, making it appear smoothly. + final Curve mainContentIncomingOpacityCurve; + + /// Curve for the opacity animation of the main content when outgoing. + /// Controls how the opacity of exiting main content changes, making it disappear smoothly. + final Curve mainContentOutgoingOpacityCurve; + + /// Curve for animating the opacity of the navigation toolbar when incoming. + /// This curve controls how the toolbar fades in as the page transitions in. + final Curve incomingNavigationToolbarOpacityCurve; + + /// Curve for animating the opacity of the navigation toolbar when outgoing. + /// Controls the fade-out effect of the toolbar as the page transitions out. + final Curve outgoingNavigationToolbarOpacityInterval; + + /// Curve for the opacity animation of the sticky action button (SAB) when incoming. + /// Defines how the SAB fades in during the page transition. + final Curve incomingSabOpacityCurve; + + /// Curve for the opacity animation of the sticky action button (SAB) when outgoing. + /// Defines how the SAB fades out during the page transition. + final Curve outgoingSabOpacityCurve; + + /// Curve for the opacity animation of the top bar when incoming. + /// Controls the fade-in effect of the top bar as the page transitions in. + final Curve incomingTopBarOpacityCurve; + + /// Curve for the opacity animation of the top bar when outgoing. + /// Controls the fade-out effect of the top bar as the page transitions out. + final Curve outgoingTopBarOpacityCurve; + + /// The beginning offset for the slide animation of the main content when it is incoming. + /// Allows specifying a custom start position, impacting how the content enters the view. + final Offset? incomingMainContentSlideBeginOffset; + + /// The ending offset for the slide animation of the main content when it is incoming. + /// Determines the final position of the entering content. + final Offset incomingMainContentSlideEndOffset; + + /// The beginning offset for the slide animation of the main content when it is outgoing. + /// Specifies the start position of the content as it begins to exit the view. + final Offset outgoingMainContentSlideBeginOffset; + + /// The ending offset for the slide animation of the main content when it is outgoing. + /// Determines the final off-screen position of the exiting content, controlling its exit trajectory. + final Offset? outgoingMainContentSlideEndOffset; + + /// 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 + /// to create smooth, visually appealing transitions. + /// + /// 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.incomingMainContentSlideBeginOffset, + this.incomingMainContentSlideEndOffset = Offset.zero, + this.outgoingMainContentSlideBeginOffset = Offset.zero, + this.outgoingMainContentSlideEndOffset, + this.modalSheetHeightTransitionCurve = const Interval( + 0 / 350, + 300 / 350, + curve: Curves.fastOutSlowIn, + ), + this.mainContentIncomingSlidePositionCurve = const Interval( + 50 / 350, + 350 / 350, + curve: Curves.fastOutSlowIn, + ), + this.mainContentOutgoingSlidePositionCurve = const Interval( + 50 / 350, + 350 / 350, + curve: Curves.fastOutSlowIn, + ), + this.mainContentIncomingOpacityCurve = const Interval( + 150 / 350, + 350 / 350, + curve: Curves.linear, + ), + this.mainContentOutgoingOpacityCurve = const Interval( + 50 / 350, + 150 / 350, + curve: Curves.linear, + ), + this.incomingNavigationToolbarOpacityCurve = const Interval( + 100 / 350, + 300 / 350, + curve: Curves.linear, + ), + this.outgoingNavigationToolbarOpacityInterval = const Interval( + 0, + 100 / 350, + curve: Curves.linear, + ), + this.incomingSabOpacityCurve = const Interval( + 100 / 350, + 300 / 350, + curve: Curves.linear, + ), + this.outgoingSabOpacityCurve = const Interval( + 0, + 100 / 350, + curve: Curves.linear, + ), + this.incomingTopBarOpacityCurve = const Interval( + 150 / 350, + 350 / 350, + curve: Curves.linear, + ), + this.outgoingTopBarOpacityCurve = const Interval( + 0, + 150 / 350, + curve: Curves.linear, + ), + }); +} diff --git a/lib/src/theme/wolt_modal_sheet_default_theme_data.dart b/lib/src/theme/wolt_modal_sheet_default_theme_data.dart index 2750b8b7..2626a838 100644 --- a/lib/src/theme/wolt_modal_sheet_default_theme_data.dart +++ b/lib/src/theme/wolt_modal_sheet_default_theme_data.dart @@ -151,4 +151,9 @@ class WoltModalSheetDefaultThemeData extends WoltModalSheetThemeData { /// Defaults to [Clip.antiAliasWithSaveLayer]. @override Clip get clipBehavior => Clip.antiAliasWithSaveLayer; + + /// Motion animation styles for both pagination and page scrolling. + @override + WoltModalSheetAnimationStyle get animationStyle => + const WoltModalSheetAnimationStyle(); } diff --git a/lib/src/theme/wolt_modal_sheet_theme_data.dart b/lib/src/theme/wolt_modal_sheet_theme_data.dart index 050415f3..c8e2f65d 100644 --- a/lib/src/theme/wolt_modal_sheet_theme_data.dart +++ b/lib/src/theme/wolt_modal_sheet_theme_data.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:wolt_modal_sheet/src/theme/wolt_modal_sheet_animation_style.dart'; import 'package:wolt_modal_sheet/src/wolt_modal_sheet.dart'; @immutable @@ -32,6 +33,7 @@ class WoltModalSheetThemeData extends ThemeExtension { this.clipBehavior, this.shadowColor, this.mainContentScrollPhysics, + this.animationStyle, }); /// The color of the surface tint overlay applied to the material color @@ -153,6 +155,9 @@ class WoltModalSheetThemeData extends ThemeExtension { /// The default value for [WoltModalSheet] scrollPhysics in the main content. final ScrollPhysics? mainContentScrollPhysics; + /// Motion animation styles for both pagination and scrolling animations. + final WoltModalSheetAnimationStyle? animationStyle; + @override WoltModalSheetThemeData copyWith({ Color? backgroundColor, @@ -179,6 +184,7 @@ class WoltModalSheetThemeData extends ThemeExtension { Color? shadowColor, Clip? clipBehavior, ScrollPhysics? mainContentScrollPhysics, + WoltModalSheetAnimationStyle? animationStyle, }) { return WoltModalSheetThemeData( backgroundColor: backgroundColor ?? this.backgroundColor, @@ -207,6 +213,7 @@ class WoltModalSheetThemeData extends ThemeExtension { clipBehavior: clipBehavior ?? this.clipBehavior, mainContentScrollPhysics: mainContentScrollPhysics ?? this.mainContentScrollPhysics, + animationStyle: animationStyle ?? this.animationStyle, ); } @@ -246,6 +253,7 @@ class WoltModalSheetThemeData extends ThemeExtension { clipBehavior: t < 0.5 ? clipBehavior : other.clipBehavior, mainContentScrollPhysics: t < 0.5 ? mainContentScrollPhysics : other.mainContentScrollPhysics, + animationStyle: t < 0.5 ? animationStyle : other.animationStyle, ); } } diff --git a/lib/wolt_modal_sheet.dart b/lib/wolt_modal_sheet.dart index 1ec3998c..40239eb3 100644 --- a/lib/wolt_modal_sheet.dart +++ b/lib/wolt_modal_sheet.dart @@ -7,3 +7,4 @@ export 'src/modal_type/wolt_modal_type.dart'; export 'src/theme/wolt_modal_sheet_theme_data.dart'; export 'src/wolt_modal_sheet.dart'; export 'src/wolt_modal_sheet_route.dart'; +export 'src/theme/wolt_modal_sheet_animation_style.dart';