| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- 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<AutoScrollListView> createState() => _AutoScrollListViewState();
- }
- class _AutoScrollListViewState extends State<AutoScrollListView> {
- 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<UserScrollNotification>(
- 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<StaggeredAutoScrollListView> createState() => _StaggeredAutoScrollListViewState();
- }
- class _StaggeredAutoScrollListViewState extends State<StaggeredAutoScrollListView> {
- final int rowCount = 3; // 列表的总行数
- final List<ScrollController> _controllers = []; // 每一行的滚动控制器
- final List<Timer?> _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);
- },
- ),
- );
- }),
- );
- }
- }
|