import 'dart:async'; import 'package:clean/utils/expand.dart'; import 'package:flutter/Material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class InfinitePreviewPageView extends StatefulWidget { final List items; final double height; final Duration autoPlayDuration; final bool autoPlay; const InfinitePreviewPageView({ super.key, required this.items, this.height = 98, this.autoPlayDuration = const Duration(seconds: 3), this.autoPlay = true, }); @override State createState() => _InfinitePreviewPageViewState(); } class _InfinitePreviewPageViewState extends State { late final PageController _pageController; Timer? _timer; double _currentPage = 0; bool _isInitialized = false; // 实际页面索引 int get _actualPage => _currentPage.round() % widget.items.length; @override void initState() { super.initState(); // 设置初始页面 final initialPage = widget.items.length * 1000; _currentPage = initialPage.toDouble(); // 设置初始currentPage _pageController = PageController( viewportFraction: 0.8, initialPage: initialPage, ); _pageController.addListener(() { setState(() { _currentPage = _pageController.page ?? initialPage.toDouble(); }); }); // 延迟一帧设置初始化完成 WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { _isInitialized = true; }); }); if (widget.autoPlay) { _startAutoPlay(); } } void _startAutoPlay() { _timer?.cancel(); _timer = Timer.periodic(widget.autoPlayDuration, (timer) { if (widget.items.isNotEmpty) { _pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); } }); } @override void dispose() { _pageController.dispose(); _timer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { if (widget.items.isEmpty) return const SizedBox(); return SizedBox( height: widget.height, child: PageView.builder( controller: _pageController, itemBuilder: (context, index) { final itemIndex = index % widget.items.length; // 如果还没初始化完成,不应用缩放效果 if (!_isInitialized) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Container( decoration: BoxDecoration( color: const Color(0xFF1E1E1E), borderRadius: BorderRadius.circular(16), ), child: widget.items[itemIndex], ), ); } // 计算缩放和透明度 double diffScale = (_currentPage - index).abs(); double scale = 1 - (diffScale * 0.1).clamp(0.0, 0.3); double opacity = 1 - (diffScale * 0.3).clamp(0.0, 0.5); return Transform.scale( scale: scale, child: Opacity( opacity: opacity, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 0), child: Container( decoration: BoxDecoration( color: const Color(0xFF1E1E1E), borderRadius: BorderRadius.circular(23.r), ), child: widget.items[itemIndex], ), ), ), ); }, ), ); } } // 单个项目组件 class PreviewItem extends StatelessWidget { final String title; final Image icon; final VoidCallback? onTap; const PreviewItem({ super.key, required this.title, required this.icon, this.onTap, }); @override Widget build(BuildContext context) { return InkWell( onTap: onTap, // borderRadius: BorderRadius.circular(16), child: Container( padding: EdgeInsets.only(left: 16.w, right: 16.w), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [ '#36363D'.color, '#18181F'.color, ], ), borderRadius: BorderRadius.circular(23.r), ), child: Row( children: [ Container( width: 56.w, height: 56.w, child: icon, ), SizedBox(width: 10.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( title, style: TextStyle( color: Colors.white, fontSize: 15.sp, fontWeight: FontWeight.w500, ), ), ], ), ), ], ), ), ); } }