flutter 自定义走马灯,内部为Widget控件的走马灯效果

import 'package:atui/generated/l10n.dart';
import 'package:atui/jade/configs/PathConfig.dart';
import 'package:atui/jade/experienceStation/ExperienceStationListTwo.dart';
import 'package:atui/jade/market/MarketExpBannerItemView.dart';
import 'package:atui/jade/market/MarketExpGoodsListPage.dart';
import 'package:atui/jade/market/MarketPromoExpBannerItemView.dart';
import 'package:atui/jade/market/promotionExpress/MarketProductSelectionBannerItemView.dart';
import 'package:atui/jade/market/promotionExpress/PromotionExpress.dart';
import 'package:atui/jade/utils/JadeColors.dart';
import 'package:atui/model/home/recommend_model.dart';
import 'package:atui/model/main_model.dart';
import 'package:atui/pages/login_page.dart';
import 'package:atui/util/easy_loading_util.dart';
import 'package:atui/util/login_util.dart';
import 'package:atui/util/navigator_util.dart';
import 'package:atui/util/provider_util.dart';
import 'package:card_swiper/card_swiper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
/*
* 商城主页面促销速递、体验购轮播、选品广场 走马灯
* */
class SmoothInfiniteScroll extends StatefulWidget {
  final RecommendModel recommendModel;
  const SmoothInfiniteScroll(this.recommendModel);

  
  State<SmoothInfiniteScroll> createState() => _SmoothInfiniteScrollState();
}

class _SmoothInfiniteScrollState extends State<SmoothInfiniteScroll> with SingleTickerProviderStateMixin {
  late ScrollController _scrollController;
  late AnimationController _animationController;
  late Animation<double> _animation;

  // 原始数据
  late final List<Widget> _originalItems;
  late List<Widget> _items; // 翻倍后的数据

  // 配置参数
  final double _itemWidth = 220;
  final double _itemSpacing = 10;
  final Duration _scrollDuration = const Duration(seconds: 45); // 滚动一轮时间

  // 原始数据总宽度(用于判断过渡点)
  double get _originalTotalWidth => _originalItems.length * (_itemWidth + _itemSpacing);

  
  void initState() {
    super.initState();
    _originalItems = [_promoBanner(), _aTuiExpBanner(),_productSelectionBanner()];
    _items = [..._originalItems, ..._originalItems..._originalItems]; // 数据翻倍,消除滚动到最后一条继续滚动出现空白区域跳回开头产品明显滚动断层问题,结合位置重置(jumpTo())实现无缝衔接

    _scrollController = ScrollController()
      ..addListener(_handleScroll);

    // 动画控制器(匀速滚动)
    _animationController = AnimationController(
      vsync: this,
      duration: _scrollDuration,
    );

    // 线性动画(确保速度均匀)
    _animation = CurvedAnimation(
      parent: _animationController,
      curve: Curves.linear,
    )..addListener(() {
      if (_scrollController.hasClients) {
        // 计算目标位置(基于动画进度)
        final maxExtent = _scrollController.position.maxScrollExtent;
        final target = _animation.value * maxExtent;
        _scrollController.jumpTo(target);
      }
    });

    _animationController.repeat();
  }

  // 处理滚动:平滑过渡替代生硬跳转
  void _handleScroll() {
    if (!_scrollController.hasClients) return;

    final currentOffset = _scrollController.offset;

    // 当滚动超过原始数据总宽度时,计算偏移差并平滑过渡
    if (currentOffset >= _originalTotalWidth) {
      // 计算当前位置与原始数据末尾的差值(这部分是需要衔接的偏移量)
      final offsetDiff = currentOffset - _originalTotalWidth;
      // 平滑滚动到差值位置(视觉上相当于从末尾回到开头的偏移处)
      _scrollController.animateTo(
        offsetDiff,
        duration: Duration(microseconds: 1), // 瞬间过渡(视觉无感知)
        curve: Curves.linear,
      );
    }
  }

  
  void dispose() {
    _scrollController.dispose();
    _animationController.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return /*MouseRegion(
      opaque: false,
      onEnter: (_) => _animationController.stop(), // 悬停暂停
      onExit: (_) => _animationController.repeat(), // 离开继续
      child: ,
    )*/
    Container(
        height: 320.w,
        margin: EdgeInsets.only(left: 30.w,right: 30.w,top: 30.w),
        padding: EdgeInsets.only(left: 15.w,right: 15.w,top: 20.w,bottom: 20.w),
        decoration: BoxDecoration(
            color: Colors.white,
            image: DecorationImage(
                image: AssetImage(PathConfig.imageMarketPromoExpBg),
                fit: BoxFit.fill
            ),
            boxShadow: [BoxShadow(color: JadeColors.grey_6,blurRadius: 1.0,offset: Offset(0.0, 0.0))]
        ),
        child:ListView.builder(
            controller: _scrollController,
            scrollDirection: Axis.horizontal,
            physics: const NeverScrollableScrollPhysics(),
            itemCount: _items.length,
            itemBuilder: (context, index) {
              return GestureDetector(
                onTap: (){
                  esLoadingToast('${_items[index]}');
                },
                child: Container(
                		color: Colors.primaries[index % Colors.primaries.length],
                    width: _itemWidth,
                    margin: EdgeInsets.only(right: _itemSpacing),
                    child: _items[index]
                ),
              );
            },
          ),
      );
  }
  
  //内部需要滚动的具体模块布局
  _promoBanner(){return Container() }
 _aTuiExpBanner(){return Container() }
 _productSelectionBanner(){return Container() }
}

引用(如果需要从外部传入参数渲染显示数据,需保证用到的参数不为空,因为initState()只调用一遍,而在数据请求未完成时一般早已执行了initState(),这会导致走马灯内部数据为空)

if (_recommendModel.promotionExpressList != null
              && _recommendModel.marketExpList != null
              && _recommendModel.productSelectionGoodsList.isNotEmpty)
          SmoothInfiniteScroll(_recommendModel),
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值