import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class AutoScrollListView extends StatefulWidget { final NullableIndexedWidgetBuilder itemBuilder; final int itemCount; final Axis scrollDirection; final EdgeInsetsGeometry? padding; final bool isAutoScrolling; const AutoScrollListView({ super.key, required this.itemBuilder, required this.itemCount, this.padding, this.isAutoScrolling = true, this.scrollDirection = Axis.horizontal, }); @override State createState() => _AutoScrollListViewState(); } class _AutoScrollListViewState extends State { final ScrollController scrollController = ScrollController(); Timer? _timer; bool _isUserScrolling = true; @override void initState() { super.initState(); _isUserScrolling = widget.isAutoScrolling; _startAutoScroll(); } @override void dispose() { super.dispose(); scrollController.dispose(); _timer?.cancel(); } void _startAutoScroll() { _timer = Timer.periodic(Duration(milliseconds: 50), (timer) { if (!_isUserScrolling || !scrollController.hasClients) return; final maxScrollExtent = scrollController.position.maxScrollExtent; final current = scrollController.position.pixels; double next = current + 1; if (next >= maxScrollExtent) { next = 0; } scrollController.jumpTo(next); }); } void _onUserScroll() { if (!_isUserScrolling) { return; } _timer?.cancel(); _timer = Timer(Duration(seconds: 1), () { setState(() { _isUserScrolling = widget.isAutoScrolling; }); _startAutoScroll(); }); } @override Widget build(BuildContext context) { return NotificationListener( onNotification: (notification) { if (notification.direction != ScrollDirection.idle) { _onUserScroll(); // 用户滑动时暂停自动滚动 } return false; // 不阻止子组件的事件传递 }, child: ListView.builder( padding: widget.padding, scrollDirection: widget.scrollDirection, itemCount: 1000000, itemBuilder: (context, index) { return widget.itemBuilder(context, index % widget.itemCount); }, controller: scrollController, ), ); } } // 多行横向自动滚动列表 /// 一个支持自动滚动的多行横向列表,每一行自动向右滚动(模拟跑马灯效果) class StaggeredAutoScrollListView extends StatefulWidget { final NullableIndexedWidgetBuilder itemBuilder; final int itemCount; final EdgeInsetsGeometry? padding; final bool isAutoScrolling; const StaggeredAutoScrollListView({ super.key, required this.itemBuilder, required this.itemCount, this.padding, this.isAutoScrolling = true, }); @override State createState() => _StaggeredAutoScrollListViewState(); } class _StaggeredAutoScrollListViewState extends State { final int rowCount = 3; final int loopFactor = 3; late final List _controllers; late final List _timers; late final List _resumeTimers; late final List _isUserScrollingList; @override void initState() { super.initState(); _controllers = List.generate(rowCount, (_) => ScrollController()); _timers = List.generate(rowCount, (_) => null); _resumeTimers = List.generate(rowCount, (_) => null); _isUserScrollingList = List.generate(rowCount, (_) => false); if (widget.isAutoScrolling) { WidgetsBinding.instance.addPostFrameCallback((_) => _startAllScrolls()); } } @override void dispose() { for (final controller in _controllers) { controller.dispose(); } for (final timer in _timers) { timer?.cancel(); } for (final timer in _resumeTimers) { timer?.cancel(); } super.dispose(); } void _startAllScrolls() { for (int i = 0; i < rowCount; i++) { _startScroll(i); } } void _startScroll(int rowIndex) { _timers[rowIndex]?.cancel(); _timers[rowIndex] = Timer.periodic(const Duration(milliseconds: 80), (_) { if (!widget.isAutoScrolling || _isUserScrollingList[rowIndex]) return; final controller = _controllers[rowIndex]; if (!controller.hasClients) return; final maxExtent = controller.position.maxScrollExtent; final current = controller.position.pixels; final next = current + 1; if (next >= maxExtent) { final viewportWidth = controller.position.viewportDimension; final contentWidth = maxExtent + viewportWidth; final originalContentWidth = contentWidth / loopFactor; final offset = next - maxExtent; controller.jumpTo(originalContentWidth + offset); } else { controller.jumpTo(next); } }); } void _onUserScroll(int rowIndex) { if (_isUserScrollingList[rowIndex]) return; _isUserScrollingList[rowIndex] = true; _timers[rowIndex]?.cancel(); _timers[rowIndex] = null; _resumeTimers[rowIndex]?.cancel(); _resumeTimers[rowIndex] = Timer(const Duration(seconds: 1), () { _isUserScrollingList[rowIndex] = false; if (widget.isAutoScrolling) { _startScroll(rowIndex); } }); } @override Widget build(BuildContext context) { final actualItemsPerRow = (widget.itemCount / rowCount).ceil(); return Column( children: List.generate(rowCount, (rowIndex) { final startIndex = rowIndex * actualItemsPerRow; final endIndex = (startIndex + actualItemsPerRow).clamp(0, widget.itemCount); final rowItems = List.generate(endIndex - startIndex, (i) => startIndex + i); final extendedItems = List.generate( rowItems.length * loopFactor, (i) => rowItems[i % rowItems.length], ); return SizedBox( height: 50.w, // 使用你自己的高度或比例 child: NotificationListener( onNotification: (notification) { if (notification.direction != ScrollDirection.idle) { _onUserScroll(rowIndex); } return false; }, child: ListView.builder( controller: _controllers[rowIndex], scrollDirection: Axis.horizontal, padding: widget.padding, itemCount: extendedItems.length, itemBuilder: (context, index) => widget.itemBuilder(context, extendedItems[index]), ), ), ); }), ); } }