| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- 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 {
- 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 int loopFactor = 3;
- late final List<ScrollController> _controllers;
- late final List<Timer?> _timers;
- late final List<Timer?> _resumeTimers;
- late final List<bool> _isUserScrollingList;
- @override
- void initState() {
- super.initState();
- _controllers = List.generate(rowCount, (_) => ScrollController());
- _timers = List.generate(rowCount, (_) => null);
- _resumeTimers = List.generate(rowCount, (_) => null);
- _isUserScrollingList = List.generate(rowCount, (_) => false);
- if (widget.isAutoScrolling) {
- WidgetsBinding.instance.addPostFrameCallback((_) => _startAllScrolls());
- }
- }
- @override
- void dispose() {
- for (final controller in _controllers) {
- controller.dispose();
- }
- for (final timer in _timers) {
- timer?.cancel();
- }
- for (final timer in _resumeTimers) {
- timer?.cancel();
- }
- super.dispose();
- }
- void _startAllScrolls() {
- for (int i = 0; i < rowCount; i++) {
- _startScroll(i);
- }
- }
- void _startScroll(int rowIndex) {
- _timers[rowIndex]?.cancel();
- _timers[rowIndex] = Timer.periodic(const Duration(milliseconds: 80), (_) {
- if (!widget.isAutoScrolling || _isUserScrollingList[rowIndex]) return;
- final controller = _controllers[rowIndex];
- 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);
- }
- });
- }
- void _onUserScroll(int rowIndex) {
- if (_isUserScrollingList[rowIndex]) return;
- _isUserScrollingList[rowIndex] = true;
- _timers[rowIndex]?.cancel();
- _timers[rowIndex] = null;
- _resumeTimers[rowIndex]?.cancel();
- _resumeTimers[rowIndex] = Timer(const Duration(seconds: 1), () {
- _isUserScrollingList[rowIndex] = false;
- if (widget.isAutoScrolling) {
- _startScroll(rowIndex);
- }
- });
- }
- @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: NotificationListener<UserScrollNotification>(
- onNotification: (notification) {
- if (notification.direction != ScrollDirection.idle) {
- _onUserScroll(rowIndex);
- }
- return false;
- },
- child: ListView.builder(
- controller: _controllers[rowIndex],
- scrollDirection: Axis.horizontal,
- padding: widget.padding,
- itemCount: extendedItems.length,
- itemBuilder: (context, index) =>
- widget.itemBuilder(context, extendedItems[index]),
- ),
- ),
- );
- }),
- );
- }
- }
|