auto_scroll_list_view.dart 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import 'dart:async';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/rendering.dart';
  4. import 'package:flutter_screenutil/flutter_screenutil.dart';
  5. class AutoScrollListView extends StatefulWidget {
  6. final NullableIndexedWidgetBuilder itemBuilder;
  7. final int itemCount;
  8. final Axis scrollDirection;
  9. final EdgeInsetsGeometry? padding;
  10. final bool isAutoScrolling;
  11. const AutoScrollListView({
  12. super.key,
  13. required this.itemBuilder,
  14. required this.itemCount,
  15. this.padding,
  16. this.isAutoScrolling = true,
  17. this.scrollDirection = Axis.horizontal,
  18. });
  19. @override
  20. State<AutoScrollListView> createState() => _AutoScrollListViewState();
  21. }
  22. class _AutoScrollListViewState extends State<AutoScrollListView> {
  23. final ScrollController scrollController = ScrollController();
  24. Timer? _timer;
  25. bool _isUserScrolling = true;
  26. @override
  27. void initState() {
  28. super.initState();
  29. _isUserScrolling = widget.isAutoScrolling;
  30. _startAutoScroll();
  31. }
  32. @override
  33. void dispose() {
  34. super.dispose();
  35. scrollController.dispose();
  36. _timer?.cancel();
  37. }
  38. void _startAutoScroll() {
  39. _timer = Timer.periodic(Duration(milliseconds: 50), (timer) {
  40. if (!_isUserScrolling || !scrollController.hasClients) return;
  41. final maxScrollExtent = scrollController.position.maxScrollExtent;
  42. final current = scrollController.position.pixels;
  43. double next = current + 1;
  44. if (next >= maxScrollExtent) {
  45. next = 0;
  46. }
  47. scrollController.jumpTo(next);
  48. });
  49. }
  50. void _onUserScroll() {
  51. if (!_isUserScrolling) {
  52. return;
  53. }
  54. _timer?.cancel();
  55. _timer = Timer(Duration(seconds: 1), () {
  56. setState(() {
  57. _isUserScrolling = widget.isAutoScrolling;
  58. });
  59. _startAutoScroll();
  60. });
  61. }
  62. @override
  63. Widget build(BuildContext context) {
  64. return NotificationListener<UserScrollNotification>(
  65. onNotification: (notification) {
  66. if (notification.direction != ScrollDirection.idle) {
  67. _onUserScroll(); // 用户滑动时暂停自动滚动
  68. }
  69. return false; // 不阻止子组件的事件传递
  70. },
  71. child: ListView.builder(
  72. padding: widget.padding,
  73. scrollDirection: widget.scrollDirection,
  74. itemCount: 1000000,
  75. itemBuilder: (context, index) {
  76. return widget.itemBuilder(context, index % widget.itemCount);
  77. },
  78. controller: scrollController,
  79. ),
  80. );
  81. }
  82. }
  83. // 多行横向自动滚动列表
  84. /// 一个支持自动滚动的多行横向列表,每一行自动向右滚动(模拟跑马灯效果)
  85. class StaggeredAutoScrollListView extends StatelessWidget {
  86. final NullableIndexedWidgetBuilder itemBuilder;
  87. final int itemCount;
  88. final EdgeInsetsGeometry? padding;
  89. final bool isAutoScrolling;
  90. final int rowCount;
  91. final int loopFactor;
  92. final double rowHeight;
  93. const StaggeredAutoScrollListView({
  94. super.key,
  95. required this.itemBuilder,
  96. required this.itemCount,
  97. this.padding,
  98. this.isAutoScrolling = true,
  99. this.rowCount = 3,
  100. this.loopFactor = 3,
  101. this.rowHeight = 50.0,
  102. });
  103. @override
  104. Widget build(BuildContext context) {
  105. final itemsPerRow = (itemCount / rowCount).ceil();
  106. return Column(
  107. children: List.generate(rowCount, (rowIndex) {
  108. final startIndex = rowIndex * itemsPerRow;
  109. final endIndex = (startIndex + itemsPerRow).clamp(0, itemCount);
  110. final rowItems = List.generate(endIndex - startIndex, (i) => startIndex + i);
  111. final extendedItems = List.generate(
  112. rowItems.length * loopFactor,
  113. (i) => rowItems[i % rowItems.length],
  114. );
  115. return SizedBox(
  116. height: rowHeight.w,
  117. child: AutoScrollListView(
  118. itemBuilder: (context, index) => itemBuilder(context, extendedItems[index]),
  119. itemCount: extendedItems.length,
  120. scrollDirection: Axis.horizontal,
  121. isAutoScrolling: isAutoScrolling,
  122. padding: padding,
  123. ),
  124. );
  125. }),
  126. );
  127. }
  128. }