diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b5aba4..5c1a3f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [1.0.4] - Dec 9,2019 +* Resolved bug for [initalOverlayWidget == null] +* One of the three callbacks and list of panels is compulsory +* added documentation + ## [1.0.3] - Dec 8,2019 * Updated description diff --git a/README.md b/README.md index 31f24e8..b506ab4 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A flutter package to implement **ScrollyTelling** in your flutter app. Using Scr ```yaml dependencies: - scrollytell: ^1.0.3 + scrollytell: ^1.0.4 ``` ### ⚡️ Import @@ -28,9 +28,9 @@ import 'package:scrollytell/scrollytell.dart'; | props | type | default value | Description | | :------------ |:---------------:| :------------:| :------------:| | panels (required) | List Widget | | A list of panel widgets | -| panelStartCallback (required) | Function(num, Function) | | Called on start of new panel | -| panelEndCallback (required) | Function(num, Function) | |Called on end of existing panel | -| panelProgressCallback (required) | Function(num, double, Function) | |Called every frame | +| panelStartCallback | Function(num, Function) | | Called on start of new panel | +| panelEndCallback| Function(num, Function) | |Called on end of existing panel | +| panelProgressCallback | Function(num, double, Function) | |Called every frame | | opacity | double | 1 | Set opacity of the panels | | lastPanelForceComplete | bool | false | Set true if the last panel hits bottom of the screen and can't be scrolled through | | initialOverlayWidget | Widget | none | Overlay widget before start of scrolling | @@ -38,6 +38,7 @@ import 'package:scrollytell/scrollytell.dart'; | stickyChartIndex | int | null | The panel of corresponding index will dock at center when scrolled past the center | | showDebugConsole | bool | false | show debug console (activePanelIndex, progress) and debug line (guideline) | +**At least one of the panelStartCallback, panelProgressCallback, panelEndCallback must be non-null.** ## Terminology and Explanation diff --git a/example/README.md b/example/README.md index 5014e67..3dbf556 100644 --- a/example/README.md +++ b/example/README.md @@ -4,5 +4,5 @@ These examples explain how to use the library. | ----------- | --------- | | Show basic usage of ScrollyWidget | [code](https://github.com/mdg-iitr/scrollytell/tree/master/example/basic_usage) | | Fancy Panel used in Animal Farm | [code](https://github.com/mdg-iitr/scrollytell/tree/master/example/animal_farm) | -| Life in Image | [code](https://github.com/mdg-iitr/scrollytell/tree/master/example/story_teller) | +| Story Telling app | [code](https://github.com/mdg-iitr/scrollytell/tree/master/example/story_teller) | diff --git a/example/basic_usage/lib/main.dart b/example/basic_usage/lib/main.dart index 7717cc5..80569e0 100644 --- a/example/basic_usage/lib/main.dart +++ b/example/basic_usage/lib/main.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:scrollytell/scrollytell.dart'; - void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @@ -52,7 +51,6 @@ class _MyHomePageState extends State { double rad; Widget _scrollyWidget = ScrollyWidget( - showDebugConsole: true, guidelinePosition: GuidelinePosition.center, opacity: 0.5, @@ -151,6 +149,9 @@ class _MyHomePageState extends State { panelStartCallback: (activePanelNumber, func) { print('panel start callback $activePanelNumber'); }, + panelEndCallback: (endingPanelNumber, func) { + print('panel end callback $endingPanelNumber'); + }, lastPanelForceComplete: true, ); diff --git a/example/basic_usage/lib/scrollytell.dart b/example/basic_usage/lib/scrollytell.dart deleted file mode 100644 index 8e307ad..0000000 --- a/example/basic_usage/lib/scrollytell.dart +++ /dev/null @@ -1,385 +0,0 @@ -library scrollytell; - -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - -// Using ScrollController instead of ScrollNotification - https://api.flutter.dev/flutter/widgets/ScrollNotification-class.html - -enum GuidelinePosition { top, center, bottom } - -class ScrollyWidget extends StatefulWidget { - ScrollyWidget({ - @required this.panels, - this.panelStartCallback, - this.panelEndCallback, - this.panelProgressCallback, - this.lastPanelForceComplete = false, - this.opacity = 1, - this.initialOverlayWidget, - this.guidelinePosition = GuidelinePosition.top, - this.showDebugConsole = false, - this.stickyChartIndex, - }) : assert(panels != null, "The list of panels cannot be null."), - assert( - panelStartCallback != null || - panelEndCallback != null || - panelProgressCallback != null, - "At least one of the panelStartCallback, panelProgressCallback, panelEndCallback must be non-null."); - - //callbacks - - final Function(num, Function) panelStartCallback; - final Function(num, Function) panelEndCallback; - final Function(num, double, Function) panelProgressCallback; - - //panelList - final List panels; - - //Set to TRUE if the last panel hits bottom of the screen hence prohibiting the scroll and want to enable complete scroll - final bool lastPanelForceComplete; - - //Overlay opacity - final double opacity; - - //Initial overlay widget - final Widget initialOverlayWidget; - - //Guideline position enum - final GuidelinePosition guidelinePosition; - - final showDebugConsole; - - final int stickyChartIndex; - - @override - _ScrollyWidgetState createState() => _ScrollyWidgetState(panels); -} - -class _ScrollyWidgetState extends State { - _ScrollyWidgetState(panels) - : this._keys = new List.generate( - panels.length, (_) => new GlobalKey<_PanelWidgetState>()); - - // User controls the overlay widget state through the callbacks - Widget _overLayWidget; - - // Index of active panel - int _activePanelIndex; - - // progress indicator [0,1] - num _progress; - - // Scroll controller - ScrollController _scrollController; - - // panel height, Using Map to maintain proper mapping(panelNumber, panelHeight) at all the time - Map _panelHeights; - List _panelPrefixHeights; - - int _heightFillIndex; - - final _keys; - - final _stackKey = GlobalKey(); - - double _offsetBias; - - bool _stickyVisibility; - - void _scrollListener() { - if (_heightFillIndex != -1) { - double filled = 0.0; - for (int i = 0; i < _heightFillIndex; i++) { - filled += _panelHeights[i]; - } - if (_scrollController.offset + _offsetBias >= filled) { - _getHeight(); - } - } - int i = 0; -// var currentOffset = _scrollController.offset; - var currentOffset = _scrollController.offset + _offsetBias; - while (i < _panelPrefixHeights.length - 1 && - currentOffset >= _panelPrefixHeights[i]) { - i++; - } - - var progressOffset = currentOffset - _panelPrefixHeights[i - 1]; - var previousPanelIndex = _activePanelIndex ?? -1; - var previousProgress = _progress; - - // taking four digit after decimal for precision - // if we take lesser than this the the callback is called several time on - // same progress - - setState(() { - _activePanelIndex = i; - _progress = i == 0 - ? num.parse( - (progressOffset / _panelPrefixHeights[i]).toStringAsFixed(4)) - : num.parse((progressOffset / - (_panelPrefixHeights[i] - _panelPrefixHeights[i - 1])) - .toStringAsFixed(4)); - }); -// print('panel index: $_activePanelIndex ,progress : $_progress '); - - if (previousPanelIndex != _activePanelIndex && - widget.panelStartCallback != null) { - widget.panelStartCallback(_activePanelIndex, - (newOverlay) => {this.setState(() => _overLayWidget = newOverlay)}); - } - - // Dart do not have tuple or pair class so returning list of two num element here - if (widget.panelProgressCallback != null) { - widget.panelProgressCallback(_activePanelIndex, _progress, - (newOverlay) => {this.setState(() => _overLayWidget = newOverlay)}); - } - - if (previousPanelIndex == _activePanelIndex && - widget.panelEndCallback != null && - previousProgress < 0.97 && - _progress >= 0.97 && - _progress < 1.0) { - widget.panelEndCallback(_activePanelIndex, - (newOverlay) => {this.setState(() => _overLayWidget = newOverlay)}); - } - - if (widget.stickyChartIndex != null && - _activePanelIndex == 4 && - previousProgress < 0.5 && - _progress >= 0.5 && - _progress < .7) { - setState(() { - _overLayWidget = Align( - alignment: Alignment.center, - child: widget.panels[widget.stickyChartIndex - 1], - ); - _stickyVisibility = false; - }); - } - if (widget.stickyChartIndex != null && - _activePanelIndex == 4 && - previousProgress > 0.5 && - _progress >= 0.3 && - _progress < .5) { - setState(() { - _stickyVisibility = true; - _overLayWidget = Container(); - }); - if (widget.panelProgressCallback != null) { - widget.panelProgressCallback(_activePanelIndex, _progress, - (newOverlay) => {this.setState(() => _overLayWidget = newOverlay)}); - } - } - } - - @override - void initState() { - // Scroll Controller and Listener - _scrollController = ScrollController(); - _scrollController.addListener(_scrollListener); - - //initializing the list, - _panelHeights = new Map(); - - //Call after render - WidgetsBinding.instance.addPostFrameCallback((_) => _getHeight()); - WidgetsBinding.instance.addPostFrameCallback((_) => _getStackHeight()); - - //initializing active panel index to 1 and progress to 0 - _activePanelIndex = 1; - _progress = 0.0; - _offsetBias = 0; - - if (widget.initialOverlayWidget != null) { - _overLayWidget = widget.initialOverlayWidget; - } else { - _overLayWidget = Container(); - } - - _stickyVisibility = true; - - super.initState(); - } - - @override - Widget build(BuildContext context) { - // The stack paints its children in order with the first child being at the bottom. - - return Stack( - key: _stackKey, - children: [ - Positioned( - child: _overLayWidget, - ), - CustomScrollView( - controller: _scrollController, - //TODO: (Later) Provide flexibility to directly input sliverList - slivers: [ - SliverList( - delegate: - SliverChildBuilderDelegate((BuildContext context, int index) { - if (widget.stickyChartIndex != null && - index == widget.stickyChartIndex - 1) { - return Opacity( - opacity: widget.opacity, - child: PanelWidget( - key: _keys[index], - rawPanel: widget.panels[index], - visible: _stickyVisibility, - ), - ); - } else { - return Opacity( - opacity: widget.opacity, - child: PanelWidget( - key: _keys[index], - rawPanel: widget.panels[index], - visible: true, - ), - ); - } - }, childCount: widget.panels.length), - ), - widget.lastPanelForceComplete - ? SliverFillRemaining() - : SliverToBoxAdapter( - child: Container(), - ) - ], - ), - Visibility( - visible: widget.showDebugConsole, - child: Align( - alignment: Alignment.topLeft, - child: Container( - color: Colors.grey, - width: 100, - child: Text( - 'active panel : $_activePanelIndex \nprogress : $_progress'), - ), - ), - ), - Visibility( - visible: widget.showDebugConsole, - child: Align( - alignment: _getAlignment(), - child: Divider( - height: 0, - color: Colors.black87, - thickness: 1.0, - ), - ), - ) - ], - ); - } - - void _getHeight() { - final List states = List.generate( - widget.panels.length, (index) => _keys[index].currentState); - - final List boxes = List.generate(widget.panels.length, - (index) => states[index]?.context?.findRenderObject()); - - setState(() { - _panelHeights = List.generate(widget.panels.length, (index) { - if (_panelHeights[index] == null) { - return boxes[index]?.size?.height; - } else { - return _panelHeights[index]; - } - }).asMap(); - }); - - final nullIndex = _getNullIndex(); - - setState(() { - _heightFillIndex = nullIndex; - }); - - _calculatePanelPrefixHeight(); - } - - _getNullIndex() { - for (var i = 0; i < _panelHeights.length; i++) { - if (_panelHeights[i] == null) { - return i; - } - } - - return -1; - } - - void _calculatePanelPrefixHeight() { - List pph = List.generate(widget.panels.length + 1, (_) => 0.0); - pph[0] = 0.0; - for (int i = 1; i <= widget.panels.length; i++) { - if (_panelHeights[i - 1] != null) { - pph[i] = pph[i - 1] + _panelHeights[i - 1]; - } else { - pph[i] = pph[i - 1]; - } - } - setState(() { - _panelPrefixHeights = pph; - }); - } - - _getStackHeight() { - final context = _stackKey.currentContext; - - double bias; - - switch (widget.guidelinePosition) { - case GuidelinePosition.top: - bias = 0; - break; - case GuidelinePosition.center: - bias = context.size.height / 2; - break; - case GuidelinePosition.bottom: - bias = context.size.height; - break; - default: - bias = 0; - break; - } - - setState(() { - _offsetBias = bias; - }); - } - - _getAlignment() { - Alignment alignment; - if (widget.guidelinePosition == GuidelinePosition.bottom) { - alignment = Alignment.bottomCenter; - } else if (widget.guidelinePosition == GuidelinePosition.center) { - alignment = Alignment.center; - } else { - alignment = Alignment.topCenter; - } - return alignment; - } -} - -class PanelWidget extends StatefulWidget { - PanelWidget({Key key, Widget rawPanel, bool visible}) - : rawPanel = rawPanel, - visible = visible, - super(key: key); - - final Widget rawPanel; - final bool visible; - - @override - _PanelWidgetState createState() => _PanelWidgetState(); -} - -class _PanelWidgetState extends State { - @override - Widget build(BuildContext context) { - return widget.visible ? widget.rawPanel : Container(); - } -} diff --git a/lib/scrollytell.dart b/lib/scrollytell.dart index 5d27b50..a0e299b 100644 --- a/lib/scrollytell.dart +++ b/lib/scrollytell.dart @@ -35,7 +35,7 @@ class ScrollyWidget extends StatefulWidget { /// Called When Panel is about to end w.r.t. guideline. /// - /// panelEndCallback: (activePanelNumber, func){} + /// panelEndCallback: (endingPanelNumber, func){} /// /// At least one of the panelStartCallback, panelProgressCallback, panelEndCallback must be non-null. final Function(num, Function) panelEndCallback; diff --git a/pubspec.yaml b/pubspec.yaml index ccd5831..74b6b37 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: scrollytell description: A flutter package to implement ScrollyTelling in your flutter app. Using ScrollyTell you can have the background changing dynamically as you scroll. -version: 1.0.3 +version: 1.0.4 author: mdg-iitr homepage: https://github.com/mdg-iitr/scrollytell.git