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 { /// 子项构建器(需根据 itemIndex 构建对应组件) 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 List _controllers = []; // 每一行的滚动控制器 final List _timers = []; // 每一行对应的自动滚动定时器 final int loopFactor = 3; // 每行内容扩展的倍数,防止滚动过短 @override void initState() { super.initState(); for (int i = 0; i < rowCount; i++) { _controllers.add(ScrollController()); } if (widget.isAutoScrolling) { WidgetsBinding.instance.addPostFrameCallback((_) => _startAllScrolls()); } } @override void dispose() { // 销毁滚动控制器 for (final controller in _controllers) { controller.dispose(); } // 取消所有定时器 for (final timer in _timers) { timer?.cancel(); } super.dispose(); } /// 启动所有行的自动滚动 void _startAllScrolls() { for (int i = 0; i < rowCount; i++) { final controller = _controllers[i]; _timers.add(Timer.periodic(const Duration(milliseconds: 50), (_) { 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); } })); } } @override @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: ListView.builder( controller: _controllers[rowIndex], scrollDirection: Axis.horizontal, padding: widget.padding, itemCount: extendedItems.length, itemBuilder: (context, index) => widget.itemBuilder(context, extendedItems[index]), ), ); }), ); } }