| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- 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++) {
- _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]),
- ),
- );
- }),
- );
- }
- }
|