Line data Source code
1 : import 'package:flutter/foundation.dart'; 2 : import 'package:flutter/widgets.dart'; 3 : 4 10 : enum _LoadingStatus { loading, stable } 5 : 6 : /// Wrapper around a [Scrollable] which triggers [onEndOfPage]/[onStartOfPage] the Scrollable 7 : /// reaches to the start or end of the view extent. 8 : class LazyLoadScrollView extends StatefulWidget { 9 : /// Creates a new instance of [LazyLoadScrollView]. The parameter [child] 10 : /// must be supplied and not null. 11 1 : const LazyLoadScrollView({ 12 : Key? key, 13 : required this.child, 14 : this.onStartOfPage, 15 : this.onEndOfPage, 16 : this.onPageScrollStart, 17 : this.onPageScrollEnd, 18 : this.onInBetweenOfPage, 19 : this.scrollOffset = 100, 20 1 : }) : super(key: key); 21 : 22 : /// The [Widget] that this widget watches for changes on 23 : final Widget child; 24 : 25 : /// Called when the [child] reaches the start of the list 26 : final AsyncCallback? onStartOfPage; 27 : 28 : /// Called when the [child] reaches the end of the list 29 : final AsyncCallback? onEndOfPage; 30 : 31 : /// Called when the list scrolling starts 32 : final VoidCallback? onPageScrollStart; 33 : 34 : /// Called when the list scrolling ends 35 : final VoidCallback? onPageScrollEnd; 36 : 37 : /// Called every time the [child] is in-between the list 38 : final VoidCallback? onInBetweenOfPage; 39 : 40 : /// The offset to take into account when triggering [onEndOfPage]/[onStartOfPage] in pixels 41 : final double scrollOffset; 42 : 43 1 : @override 44 1 : State<StatefulWidget> createState() => _LazyLoadScrollViewState(); 45 : } 46 : 47 : class _LazyLoadScrollViewState extends State<LazyLoadScrollView> { 48 : var _loadMoreStatus = _LoadingStatus.stable; 49 : double _scrollPosition = 0; 50 : 51 1 : @override 52 : Widget build(BuildContext context) => 53 1 : NotificationListener<ScrollNotification>( 54 1 : onNotification: _onNotification, 55 2 : child: widget.child, 56 : ); 57 : 58 1 : bool _onNotification(ScrollNotification notification) { 59 1 : if (notification is ScrollStartNotification) { 60 2 : if (widget.onPageScrollStart != null) { 61 3 : widget.onPageScrollStart!(); 62 : return true; 63 : } 64 : } 65 1 : if (notification is ScrollEndNotification) { 66 2 : if (widget.onPageScrollEnd != null) { 67 3 : widget.onPageScrollEnd!(); 68 : return true; 69 : } 70 : } 71 1 : if (notification is ScrollUpdateNotification) { 72 2 : final pixels = notification.metrics.pixels; 73 2 : final maxScrollExtent = notification.metrics.maxScrollExtent; 74 2 : final minScrollExtent = notification.metrics.minScrollExtent; 75 2 : final scrollOffset = widget.scrollOffset; 76 : 77 2 : if (pixels > (minScrollExtent + scrollOffset) && 78 2 : pixels < (maxScrollExtent - scrollOffset)) { 79 2 : if (widget.onInBetweenOfPage != null) { 80 3 : widget.onInBetweenOfPage!(); 81 : return true; 82 : } 83 : } 84 : 85 2 : final extentBefore = notification.metrics.extentBefore; 86 2 : final extentAfter = notification.metrics.extentAfter; 87 2 : final scrollingDown = _scrollPosition < pixels; 88 : 89 : if (scrollingDown) { 90 1 : if (extentAfter <= scrollOffset) { 91 1 : _onEndOfPage(); 92 : return true; 93 : } 94 : } else { 95 1 : if (extentBefore <= scrollOffset) { 96 1 : _onStartOfPage(); 97 : return true; 98 : } 99 : } 100 : 101 1 : _scrollPosition = pixels; 102 : } 103 1 : if (notification is OverscrollNotification) { 104 2 : if (notification.overscroll > 0) { 105 1 : _onEndOfPage(); 106 : } 107 2 : if (notification.overscroll < 0) { 108 1 : _onStartOfPage(); 109 : } 110 : return true; 111 : } 112 : return false; 113 : } 114 : 115 1 : void _onEndOfPage() { 116 2 : if (_loadMoreStatus == _LoadingStatus.stable) { 117 2 : if (widget.onEndOfPage != null) { 118 1 : _loadMoreStatus = _LoadingStatus.loading; 119 5 : widget.onEndOfPage!().whenComplete(() { 120 1 : _loadMoreStatus = _LoadingStatus.stable; 121 : }); 122 : } 123 : } 124 : } 125 : 126 1 : void _onStartOfPage() { 127 2 : if (_loadMoreStatus == _LoadingStatus.stable) { 128 2 : if (widget.onStartOfPage != null) { 129 1 : _loadMoreStatus = _LoadingStatus.loading; 130 5 : widget.onStartOfPage!().whenComplete(() { 131 1 : _loadMoreStatus = _LoadingStatus.stable; 132 : }); 133 : } 134 : } 135 : } 136 : }