|
|
@@ -2,6 +2,7 @@ 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;
|
|
|
@@ -100,3 +101,124 @@ class _AutoScrollListViewState extends State<AutoScrollListView> {
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+// 多行横向自动滚动列表
|
|
|
+/// 一个支持自动滚动的多行横向列表,每一行自动向右滚动(模拟跑马灯效果)
|
|
|
+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);
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|