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++) { final controller = ScrollController(); _controllers.add(controller); } // 如果启用自动滚动,则在渲染后启动滚动逻辑 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]; // 每 50ms 滚动一次,实现平滑向右滚动效果 final timer = 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) { // 若滚动到底部,则跳转到中间,避免突然回到顶部产生视觉跳变 controller.jumpTo(maxExtent / 2); } else { controller.jumpTo(next); } }); _timers.add(timer); } } @override Widget build(BuildContext context) { // 每一行应显示的实际数据项数(取整向上) final actualItemsPerRow = (widget.itemCount / rowCount).ceil(); // 每一行扩展后的 item 数量(用于循环滚动) final extendedItemsPerRow = actualItemsPerRow * loopFactor; return Column( children: List.generate(rowCount, (rowIndex) { return SizedBox( height: 50.w, // 每行高度,单位为屏幕适配宽度 child: ListView.builder( controller: _controllers[rowIndex], // 设置对应行的滚动控制器 scrollDirection: Axis.horizontal, padding: widget.padding, itemCount: extendedItemsPerRow, // 扩展后的数量,制造无限滚动感 itemBuilder: (context, index) { // 计算实际要展示的数据项索引 final itemIndex = (rowIndex * actualItemsPerRow + index) % widget.itemCount; return widget.itemBuilder(context, itemIndex); }, ), ); }), ); } }