Flutter之自定義封裝輪播圖

圖片描述

黃沙百戰穿金甲
不破樓蘭終不還git

前言

經過封裝PageView+Timer實現無限輪播,手動拖拽時中止定時器功能,拖拽完成後開啓定時器。github

功能

  1. 自動輪播
  2. 手動輪播
  3. 指示器

功能展現

圖片描述

代碼實現

輪播組件的構造方法網絡

class Carousel extends StatefulWidget {
  final List<BannerModel> banners; // BannerModel是每一個item對應的模型 必傳參數
  final OnTapBannerItem onTap, // `必傳參數` 傳入點擊每一個item的方法
  final Color indicatorNormalColor;// 指示器球的正常顏色
  final Color indicatorCurrentColor;// 指示器球的當前顏色
  final double indicatorWidth;// 指示器球的寬高
  final double indicatorMargin;// 指示器球之間間距
  final bool  hiddenIndicator;// 是否影藏指示器
  final bool  hiddenIndicatorForSingle;// 單個圖片是否影藏指示器
  final bool  autoScroll; // 是否循環
  final int  seconds; // 輪播間隔

  Carousel(
      {Key key,
        @required this.banners,
        @required this.onTap,
        this.seconds = 5, // 不傳 默認5秒 輪播一次
        this.autoScroll = true,
        this.hiddenIndicator = false,
        this.hiddenIndicatorForSingle = true,
        this.indicatorWidth = 6,
        this.indicatorMargin = 1.5,
        this.indicatorCurrentColor = Colors.white,
        this.indicatorNormalColor = Colors.grey})
      : super(key: key);
  @override
  State<StatefulWidget> createState() {
    return _BannerState();
  }
}

組件狀態實現方法ide

class _BannerState extends State<Carousel> {

  int _currentIndex = 1;
  PageController controller = PageController(initialPage: 1, viewportFraction: 1);
  Timer _timer;
  
  @override
  void initState() {
    super.initState();
    if(widget.banners.length == 0) return;
    controller = PageController(initialPage: 1);
    if(widget.autoScroll && widget.banners.length > 1) {
      _setTimer();
    }
  }
  // 建立定時器
  _setTimer(){
    _timer = Timer.periodic(Duration(seconds: widget.seconds), (timer) { // 自動滾動
      /// print(realIndex);
      controller.animateToPage(_currentIndex + 1,
          duration: Duration(milliseconds: 300),
          curve: Curves.linear);
    });
  }

  @override
  // 頁面退出時銷燬定時器
  void dispose() {
    super.dispose();
    controller.dispose();
    _timer.cancel();
  }

  // 是否顯示指示器
  _showIndicator() {
    if(widget.banners.length == 0) return false;
    if(widget.hiddenIndicator) return false;
    if(widget.banners.length==1 && widget.hiddenIndicatorForSingle) return false;
    return true;
  }
  // pageView是否能夠滾動
  _isCanScroll() {
    if(widget.banners.length == 0 || widget.banners.length == 1) return false;
    return true;
  }

  @override
  Widget build(BuildContext context) {
    List<BannerModel> _list = List();
    if(widget.banners.length > 0) {
      _list
        ..add(widget.banners[widget.banners.length - 1])
        ..addAll(widget.banners)
        ..add(widget.banners[0]);
    }

    return widget.banners.length>0? Container(
      child: Stack(
          alignment: Alignment.bottomCenter,
          children: <Widget>[
            NotificationListener(
              onNotification: (ScrollNotification notification) {
                if(widget.autoScroll && widget.banners.length > 1) {
                  if (notification.depth == 0 &&
                      notification is ScrollStartNotification) {
                    if (notification.dragDetails != null) {
                      _timer.cancel();
                    }
                  } else if (notification is ScrollEndNotification) {
                    _timer.cancel();
                    _setTimer();
                  }
                }
              },
              child:_pageView(_list),
            ),

            _showIndicator() ? _buildIndicator() : Container(), // 下面的小點
          ]),
    ) : Container();
  }
  // 建立輪播View
  Widget _pageView(List _list) {
     return PageView(
      controller: controller,
      onPageChanged: (page) {
        int newIndex;
        if (page == _list.length - 1) {
          newIndex = 1;
          controller.jumpToPage(newIndex);
        } else if (page == 0) {
          newIndex = _list.length - 2;
          controller.jumpToPage(newIndex);
        } else {
          newIndex = page;
        }
        setState(() {
          _currentIndex = newIndex;
        });
      },
      children: _list.map((model) => _buildItem(model)).toList(),
      physics: _isCanScroll() ? AlwaysScrollableScrollPhysics() : NeverScrollableScrollPhysics(),
    );
  }
  // 建立item
  Widget _buildItem(BannerModel model) {
    Image image = Image.asset(model.image, fit: BoxFit.cover);
    if(model.url != null) image = Image.network(model.url, fit: BoxFit.cover);

    return GestureDetector(
      onTap: () { // 按下
        if (widget.onTap != null) {
          widget.onTap(model);
        }
      },
      child: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          image,
        ],
      ),
    );
  }
  // 建立指示器
  Widget _buildIndicator() {
     return Positioned(
      bottom: 15.0,
      left: 0,
      right: 0,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: widget.banners
            .asMap()
            .map((i, v) => MapEntry(
            i,
            Container(
              width: widget.indicatorWidth,
              height: widget.indicatorWidth,
              margin: EdgeInsets.only(left: 2.0, right: 2.0),
              decoration: ShapeDecoration(
                  color: _currentIndex == i + 1
                      ? widget.indicatorCurrentColor
                      : widget.indicatorNormalColor,
                  shape: CircleBorder()),
            )))
            .values
            .toList(),
      ),
    );
  }
}
typedef void OnTapBannerItem(BannerModel model);

模型實現

根據本身的須要實現對應的模型ui

class BannerModel extends Object {
      final String image;// 本地圖片路徑
      final String url; // 網絡URL
        
      // 當有網絡鏈接時會優先使用網絡圖片,沒有則使用本地圖片
      BannerModel(this.url, {this.image});// image爲可選參數,
    }

源碼地址

連接描述this

相關文章
相關標籤/搜索