LCOV - code coverage report
Current view: top level - lib/Provider - LiquidProvider.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 120 155 77.4 %
Date: 2021-04-02 00:29:34 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : 
       3             : import 'package:flutter/cupertino.dart';
       4             : import 'package:liquid_swipe/Helpers/Helpers.dart';
       5             : import 'package:liquid_swipe/Helpers/SlideUpdate.dart';
       6             : import 'package:liquid_swipe/PageHelpers/animated_page_dragger.dart';
       7             : import 'package:liquid_swipe/PageHelpers/page_dragger.dart';
       8             : import 'package:liquid_swipe/liquid_swipe.dart';
       9             : import 'package:provider/provider.dart';
      10             : 
      11             : /// Internal Class
      12             : ///
      13             : /// A [ChangeNotifierProvider] to manage [LiquidSwipe] State.
      14             : /// Every Change is notified from it.
      15             : /// Methods Included :
      16             : ///  -  [animateToPage]
      17             : ///  -  [_animateDirectlyToPage]
      18             : ///  -  [jumpToPage]
      19             : ///  -  [updateData]
      20             : ///  -  some more, soon.
      21             : class LiquidProvider extends ChangeNotifier {
      22             :   /// A [SlideUpdate] type for storing the current Slide Update.
      23             :   SlideUpdate? slideUpdate;
      24             : 
      25             :   /// [AnimatedPageDragger] required for completing the animation when [UpdateType] is [UpdateType.doneAnimating]
      26             :   late AnimatedPageDragger animatedPageDragger;
      27             : 
      28             :   /// Storing ActivePage Index
      29             :   /// default = 0
      30             :   int activePageIndex = 0;
      31             : 
      32             :   ///Storing next Page Index
      33             :   ///default = 0
      34             :   int nextPageIndex = 0;
      35             : 
      36             :   ///Storing the Swipe Direction using [SlideDirection]
      37             :   SlideDirection slideDirection = SlideDirection.none;
      38             : 
      39             :   /// percentage of slide both Horizontal and Vertical, during touch
      40             :   double slidePercentHor = 0.00;
      41             :   double slidePercentVer = 0.00;
      42             : 
      43             :   ///Storing Previous [UpdateType]
      44             :   UpdateType? prevUpdate;
      45             : 
      46             :   ///user manageable bool to make Enable and Disable loop within the Pages
      47             :   bool enableLoop = true;
      48             : 
      49             :   ///Number of Page.
      50             :   int pagesLength = 0;
      51             : 
      52             :   ///Ticker Provider from [LiquidSwipe], cause need to use it in [AnimatedPageDragger]
      53             :   late TickerProviderStateMixin singleTickerProviderStateMixin;
      54             : 
      55             :   ///SlideIcon position, always Horizontal, used in [PageDragger]
      56             :   late double positionSlideIcon;
      57             : 
      58             :   ///see [CurrentUpdateTypeCallback]
      59             :   CurrentUpdateTypeCallback? _currentUpdateTypeCallback;
      60             : 
      61             :   ///see [OnPageChangeCallback]
      62             :   OnPageChangeCallback? _onPageChangeCallback;
      63             : 
      64             :   ///see [SlidePercentCallback]
      65             :   SlidePercentCallback? _slidePercentCallback;
      66             : 
      67             :   ///bool variable to set if Liquid Swipe is currently in Progress
      68             :   bool isInProgress = false;
      69             : 
      70             :   ///bool to store is its still animating
      71             :   bool _isAnimating = false;
      72             : 
      73             :   ///A user handled value if user want, just only to use programmatic pages changes
      74             :   ///default = false
      75             :   bool shouldDisableUserGesture = false;
      76             : 
      77             :   bool enableSideReveal = false;
      78             : 
      79             :   Size iconSize = Size.zero;
      80             : 
      81             :   Timer? _timer;
      82             :   Timer? _timerInner;
      83             : 
      84             :   ///Constructor
      85             :   ///Contains Default value or Developer desired Values
      86             :   /// [initialPage] - Initial Page of the LiquidSwipe (0 - n)
      87             :   /// [loop]  - Should Enable Loop between Pages
      88             :   /// [length]  - Total Number of Pages
      89           1 :   LiquidProvider({
      90             :     required int initialPage,
      91             :     required bool loop,
      92             :     required int length,
      93             :     required TickerProviderStateMixin vsync,
      94             :     required double slideIcon,
      95             :     required OnPageChangeCallback? onPageChangeCallback,
      96             :     required CurrentUpdateTypeCallback? currentUpdateTypeCallback,
      97             :     required SlidePercentCallback? slidePercentCallback,
      98             :     required bool disableGesture,
      99             :     required bool enableSideReveal,
     100             :   }) {
     101           1 :     slidePercentHor = 0.00;
     102           1 :     slidePercentVer = 0.00;
     103           1 :     activePageIndex = initialPage;
     104           1 :     nextPageIndex = initialPage;
     105           1 :     enableLoop = loop;
     106           1 :     pagesLength = length;
     107           1 :     singleTickerProviderStateMixin = vsync;
     108           1 :     positionSlideIcon = slideIcon;
     109           1 :     _currentUpdateTypeCallback = currentUpdateTypeCallback;
     110           1 :     _slidePercentCallback = slidePercentCallback;
     111           1 :     _onPageChangeCallback = onPageChangeCallback;
     112           1 :     shouldDisableUserGesture = disableGesture;
     113           1 :     this.enableSideReveal = enableSideReveal;
     114             : 
     115           2 :     updateSlide(SlideUpdate(
     116             :       SlideDirection.rightToLeft,
     117             :       0.00,
     118           1 :       positionSlideIcon,
     119             :       UpdateType.dragging,
     120             :     ));
     121             :   }
     122             : 
     123             :   ///Animating page to the mentioned page
     124             :   ///
     125             :   ///Known Issue : First we have to jump to the previous screen.
     126             :   ///
     127             :   ///Current Behaviour : Lets say there are 3 pages,
     128             :   ///current page is Red and next is Blue and and last is Green.
     129             :   ///if [page] in this method came to ne 2 i.e., we need to animate directly to Green Page
     130             :   ///but currently I have to jump to page 1 i.e., Blue and than perform Animation using [updateSlide]
     131             :   ///
     132             :   ///Required Behaviour : I Don't want Blue to be there in between the transition of Red and Green i.e.,
     133             :   ///If we are animating [activePageIndex] should be 0 and [nextPageIndex] should be 2, which is not possible through current implementation.
     134             :   ///
     135             :   /// If you encounter this and have suggestions don't forget to raise an Issue.
     136             :   ///
     137             :   ///Not making it for Public usage for now due to the mentioned Issue
     138             :   /// _animateDirectlyToPage(int page, int duration) {
     139             :   ///   if (isInProgress || activePageIndex == page) return;
     140             :   ///   isInProgress = true;
     141             :   ///   activePageIndex = page - 1;
     142             :   ///   nextPageIndex = page;
     143             :   ///   if (activePageIndex < 0) {
     144             :   ///     activePageIndex = 0;
     145             :   ///     jumpToPage(page);
     146             :   ///     return;
     147             :   ///   }
     148             :   ///   _timer = Timer.periodic(const Duration(milliseconds: 1), (t) {
     149             :   ///     if (t.tick < duration / 2) {
     150             :   ///       updateSlide(SlideUpdate(SlideDirection.rightToLeft, t.tick / duration,
     151             :   ///           1, UpdateType.dragging));
     152             :   ///     } else if (t.tick < duration) {
     153             :   ///       updateSlide(SlideUpdate(SlideDirection.rightToLeft, t.tick / duration,
     154             :   ///           1, UpdateType.animating));
     155             :   ///     } else {
     156             :   ///       updateSlide(SlideUpdate(
     157             :   ///           SlideDirection.rightToLeft, 1, 1, UpdateType.doneAnimating));
     158             :   ///       t.cancel();
     159             :   ///       isInProgress = false;
     160             :   ///     }
     161             :   ///   });
     162             :   /// }
     163             :   ///
     164             :   ///Animating to the Page in One-by-One manner
     165             :   ///Required parameters :
     166             :   /// - [page], the page index you want to animate to.
     167             :   /// - [duration], of [Duration] type, for complete animation
     168           1 :   animateToPage(int page, int duration) {
     169           3 :     if (isInProgress || activePageIndex == page) return;
     170           1 :     isInProgress = true;
     171             :     int diff = 0;
     172           1 :     _timer?.cancel();
     173           1 :     _timerInner?.cancel();
     174           2 :     if (activePageIndex < page) {
     175           2 :       diff = page - activePageIndex;
     176           1 :       int newDuration = duration ~/ diff;
     177           3 :       _timer = Timer.periodic(Duration(milliseconds: newDuration), (callback) {
     178           0 :         _timerInner = Timer.periodic(const Duration(milliseconds: 1), (t) {
     179           0 :           if (t.tick < newDuration / 2) {
     180           0 :             updateSlide(SlideUpdate(SlideDirection.rightToLeft,
     181           0 :                 t.tick / newDuration, positionSlideIcon, UpdateType.dragging));
     182           0 :           } else if (t.tick < newDuration) {
     183           0 :             updateSlide(SlideUpdate(SlideDirection.rightToLeft,
     184           0 :                 t.tick / newDuration, positionSlideIcon, UpdateType.animating));
     185             :           } else {
     186           0 :             updateSlide(SlideUpdate(SlideDirection.rightToLeft, 1,
     187           0 :                 positionSlideIcon, UpdateType.doneAnimating));
     188           0 :             t.cancel();
     189             :           }
     190             :         });
     191           0 :         if (callback.tick >= diff) {
     192           0 :           callback.cancel();
     193           0 :           isInProgress = false;
     194             :         }
     195             :       });
     196             :     } else {
     197           2 :       diff = activePageIndex - page;
     198           1 :       int newDuration = duration ~/ diff;
     199           3 :       _timer = Timer.periodic(Duration(milliseconds: newDuration), (callback) {
     200           0 :         _timerInner = Timer.periodic(const Duration(milliseconds: 1), (t) {
     201           0 :           if (t.tick < newDuration / 2) {
     202           0 :             updateSlide(SlideUpdate(SlideDirection.leftToRight,
     203           0 :                 t.tick / newDuration, positionSlideIcon, UpdateType.dragging));
     204           0 :           } else if (t.tick < newDuration) {
     205           0 :             updateSlide(SlideUpdate(SlideDirection.leftToRight,
     206           0 :                 t.tick / newDuration, positionSlideIcon, UpdateType.animating));
     207             :           } else {
     208           0 :             updateSlide(SlideUpdate(SlideDirection.leftToRight, 1,
     209           0 :                 positionSlideIcon, UpdateType.doneAnimating));
     210           0 :             t.cancel();
     211             :           }
     212             :         });
     213           0 :         if (callback.tick >= diff) {
     214           0 :           callback.cancel();
     215           0 :           isInProgress = false;
     216             :         }
     217             :       });
     218             :     }
     219             :   }
     220             : 
     221             :   ///Directly Jump to the mentioned [page] without any animation
     222           1 :   jumpToPage(int page) {
     223           3 :     if (page == activePageIndex || isInProgress) return;
     224           4 :     if (page > pagesLength - 1 || page < 0) {
     225           0 :       throw ("Index $page not found in the Pages list");
     226             :     }
     227           1 :     isInProgress = true;
     228           2 :     activePageIndex = page - 1;
     229           1 :     nextPageIndex = page;
     230           3 :     if (nextPageIndex >= pagesLength) nextPageIndex = 0;
     231           3 :     updateSlide(SlideUpdate(SlideDirection.rightToLeft, 1, positionSlideIcon,
     232             :         UpdateType.doneAnimating));
     233           1 :     isInProgress = false;
     234             :   }
     235             : 
     236             :   ///Method to update the [slideUpdate] and it directly calls [updateData]
     237           1 :   updateSlide(SlideUpdate slidUpdate) {
     238           1 :     slideUpdate = slidUpdate;
     239           1 :     updateData(slidUpdate);
     240           1 :     notifyListeners();
     241             :   }
     242             : 
     243             :   ///updating data using [SlideUpdate], generally we are handling and managing the Animation [UpdateType]
     244             :   ///in this methods,
     245             :   ///All callbacks and factors are also managed by this method.
     246           1 :   updateData(SlideUpdate event) {
     247           4 :     if (prevUpdate != event.updateType && _currentUpdateTypeCallback != null)
     248           3 :       _currentUpdateTypeCallback!(event.updateType);
     249             : 
     250           1 :     if (_slidePercentCallback != null &&
     251           2 :         event.updateType != UpdateType.doneAnimating) {
     252           3 :       String hor = (event.slidePercentHor * 100).toStringAsExponential(2);
     253           3 :       String ver = (event.slidePercentVer * 100).toStringAsExponential(2);
     254           2 :       _slidePercentCallback!(
     255           4 :           double.parse(hor), (((double.parse(ver)) * 100) / 100));
     256             :     }
     257             : 
     258           2 :     prevUpdate = event.updateType;
     259             : 
     260             :     //if the user is dragging then
     261           2 :     if (event.updateType == UpdateType.dragging) {
     262           2 :       slideDirection = event.direction;
     263           2 :       slidePercentHor = event.slidePercentHor;
     264           2 :       slidePercentVer = event.slidePercentVer;
     265             : 
     266             :       // making pages to be in loop
     267           2 :       nextPageIndex = activePageIndex;
     268           1 :       if (enableLoop) {
     269             :         //conditions on slide direction
     270           2 :         if (slideDirection == SlideDirection.leftToRight) {
     271           3 :           nextPageIndex = activePageIndex - 1;
     272           2 :         } else if (slideDirection == SlideDirection.rightToLeft) {
     273           3 :           nextPageIndex = activePageIndex + 1;
     274             :         }
     275             : 
     276           4 :         if (nextPageIndex > pagesLength - 1) {
     277           0 :           nextPageIndex = 0;
     278           2 :         } else if (nextPageIndex < 0) {
     279           3 :           nextPageIndex = pagesLength - 1;
     280             :         }
     281             :         return;
     282             :       }
     283             : 
     284             :       //conditions on slide direction
     285           2 :       if (slideDirection == SlideDirection.leftToRight &&
     286           0 :           activePageIndex != 0) {
     287           0 :         nextPageIndex = activePageIndex - 1;
     288           2 :       } else if (slideDirection == SlideDirection.rightToLeft &&
     289           4 :           activePageIndex != pagesLength - 1) {
     290           3 :         nextPageIndex = activePageIndex + 1;
     291             :       }
     292             :       return;
     293             :     }
     294             :     //if the user has done dragging
     295           2 :     else if (event.updateType == UpdateType.doneDragging) {
     296             :       // slidepercent > 0.2 so that it wont reveal itself unless this condition is true
     297           2 :       if (slidePercentHor > 0.2) {
     298           1 :         isAnimating = true; // Page started to animate
     299             : 
     300           2 :         animatedPageDragger = AnimatedPageDragger(
     301             :           slideUpdateStream: this,
     302           1 :           slideDirection: slideDirection,
     303             :           transitionGoal: TransitionGoal.open,
     304           1 :           slidePercentHor: slidePercentHor,
     305           1 :           slidePercentVer: slidePercentVer,
     306           1 :           vsync: singleTickerProviderStateMixin,
     307             :         );
     308             :       } else {
     309           2 :         animatedPageDragger = AnimatedPageDragger(
     310             :           slideUpdateStream: this,
     311           1 :           slideDirection: slideDirection,
     312             :           transitionGoal: TransitionGoal.close,
     313           1 :           slidePercentHor: slidePercentHor,
     314           1 :           slidePercentVer: slidePercentVer,
     315           1 :           vsync: singleTickerProviderStateMixin,
     316             :         );
     317             : 
     318           2 :         nextPageIndex = activePageIndex;
     319             :       }
     320             :       //Run the animation
     321           2 :       animatedPageDragger.run();
     322             :       return;
     323             :     }
     324             :     //when animating
     325           2 :     else if (event.updateType == UpdateType.animating) {
     326           2 :       slideDirection = event.direction;
     327           2 :       slidePercentHor = event.slidePercentHor;
     328           2 :       slidePercentVer = event.slidePercentVer;
     329             :       return;
     330             :     }
     331             : 
     332             :     //done animating
     333           1 :     if (_onPageChangeCallback != null) {
     334           3 :       _onPageChangeCallback!(nextPageIndex);
     335             :     }
     336           2 :     activePageIndex = nextPageIndex;
     337           1 :     slideDirection = SlideDirection.rightToLeft;
     338           1 :     slidePercentHor = 0.00;
     339           2 :     slidePercentVer = positionSlideIcon;
     340           2 :     nextPageIndex = activePageIndex;
     341             : 
     342           1 :     if (enableLoop) {
     343             :       //conditions on slide direction
     344           2 :       if (slideDirection == SlideDirection.leftToRight) {
     345           0 :         nextPageIndex = activePageIndex - 1;
     346           2 :       } else if (slideDirection == SlideDirection.rightToLeft) {
     347           3 :         nextPageIndex = activePageIndex + 1;
     348             :       }
     349             : 
     350           4 :       if (nextPageIndex > pagesLength - 1) {
     351           1 :         nextPageIndex = 0;
     352           2 :       } else if (nextPageIndex < 0) {
     353           0 :         nextPageIndex = pagesLength - 1;
     354             :       }
     355             :     } else {
     356           2 :       if (slideDirection == SlideDirection.leftToRight &&
     357           0 :           activePageIndex != 0) {
     358           0 :         nextPageIndex = activePageIndex - 1;
     359           2 :       } else if (slideDirection == SlideDirection.rightToLeft &&
     360           4 :           activePageIndex != pagesLength - 1) {
     361           0 :         nextPageIndex = activePageIndex + 1;
     362             :       }
     363             :     }
     364             : 
     365           1 :     isAnimating = false; // Page stopped animating
     366             :     return;
     367             :   }
     368             : 
     369             :   ///Setter for [_isAnimating]
     370           1 :   set isAnimating(bool newValue) {
     371           1 :     this._isAnimating = newValue;
     372           1 :     notifyListeners();
     373             :   }
     374             : 
     375             :   ///Getter for [_isAnimating]
     376           2 :   bool get isAnimating => _isAnimating;
     377             : 
     378             :   ///Setter for [isUserGestureDisabled]
     379           1 :   set setUserGesture(bool disable) {
     380           1 :     this.shouldDisableUserGesture = disable;
     381           1 :     notifyListeners();
     382             :   }
     383             : 
     384             :   ///Setter for [isUserGestureDisabled]
     385           2 :   bool get isUserGestureDisabled => shouldDisableUserGesture;
     386             : 
     387             :   ///Method to set [iconSize]
     388           1 :   setIconSize(Size size) {
     389           1 :     iconSize = size;
     390           1 :     notifyListeners();
     391             :   }
     392             : 
     393           1 :   @override
     394             :   void dispose() {
     395           2 :     _timer?.cancel();
     396           1 :     _timerInner?.cancel();
     397           1 :     super.dispose();
     398             :   }
     399             : }

Generated by: LCOV version 1.15