infinite_scroll_view.dart 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import 'package:flutter/rendering.dart';
  2. import 'package:flutter/widgets.dart';
  3. /// The function called when the user scrolls the [InfiniteScrollView]. [offset]
  4. /// is the current offset of the scroll view, ranging from [double.negativeInfinity]
  5. /// to [double.infinity].
  6. typedef ScrollCallback = void Function(double offset);
  7. /// A [Scrollable] that can be scrolled infinitely in both directions. When
  8. /// scroll happens, the [onScroll] callback is called with the new offset.
  9. class InfiniteScrollView extends StatelessWidget {
  10. const InfiniteScrollView({
  11. super.key,
  12. required this.onScroll,
  13. required this.child,
  14. });
  15. final ScrollCallback onScroll;
  16. final Widget child;
  17. @override
  18. Widget build(BuildContext context) {
  19. return Scrollable(
  20. viewportBuilder: (context, position) {
  21. return _InfiniteScrollView(
  22. position: position,
  23. onScroll: onScroll,
  24. child: child,
  25. );
  26. },
  27. );
  28. }
  29. }
  30. class _InfiniteScrollView extends SingleChildRenderObjectWidget {
  31. const _InfiniteScrollView({
  32. // super.key,
  33. super.child,
  34. required this.position,
  35. required this.onScroll,
  36. });
  37. final ViewportOffset position;
  38. final ScrollCallback onScroll;
  39. @override
  40. RenderObject createRenderObject(BuildContext context) {
  41. return _RenderInfiniteScrollView(
  42. position: position,
  43. onScroll: onScroll,
  44. );
  45. }
  46. @override
  47. void updateRenderObject(
  48. BuildContext context,
  49. _RenderInfiniteScrollView renderObject,
  50. ) {
  51. renderObject
  52. ..position = position
  53. ..onScroll = onScroll;
  54. }
  55. }
  56. class _RenderInfiniteScrollView extends RenderShiftedBox {
  57. _RenderInfiniteScrollView({
  58. RenderBox? child,
  59. required ViewportOffset position,
  60. required ScrollCallback onScroll,
  61. }) : _position = position,
  62. _scrollCallback = onScroll,
  63. super(child);
  64. ViewportOffset _position;
  65. set position(ViewportOffset value) {
  66. if (_position == value) return;
  67. if (attached) _position.removeListener(markNeedsLayout);
  68. _position = value;
  69. if (attached) _position.addListener(markNeedsLayout);
  70. markNeedsLayout();
  71. }
  72. ScrollCallback _scrollCallback;
  73. set onScroll(ScrollCallback value) {
  74. if (_scrollCallback == value) return;
  75. _scrollCallback = value;
  76. markNeedsLayout();
  77. }
  78. void _onScroll() {
  79. _scrollCallback(_position.pixels);
  80. }
  81. @override
  82. void attach(covariant PipelineOwner owner) {
  83. super.attach(owner);
  84. _position.addListener(_onScroll);
  85. }
  86. @override
  87. void detach() {
  88. super.detach();
  89. _position.removeListener(_onScroll);
  90. }
  91. @override
  92. void performLayout() {
  93. child?.layout(constraints, parentUsesSize: true);
  94. size = child?.size ?? Size.zero;
  95. _position.applyViewportDimension(size.height);
  96. _position.applyContentDimensions(double.negativeInfinity, double.infinity);
  97. }
  98. }