共计 4021 个字符,预计需要花费 11 分钟才能阅读完成。
黄沙百战穿金甲
不破楼兰终不还
前言
通过封装 PageView+Timer 实现无限轮播, 手动拖拽时停止定时器功能,拖拽完成后开启定时器。
功能
- 自动轮播
- 手动轮播
- 指示器
功能展示
代码实现
轮播组件的构造方法
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();
}
}
组件状态实现方法
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);
模型实现
根据自己的需要实现对应的模型
class BannerModel extends Object {
final String image;// 本地图片路径
final String url; // 网络 URL
// 当有网络连接时会优先使用网络图片,没有则使用本地图片
BannerModel(this.url, {this.image});// image 为可选参数,}
源码地址
链接描述
正文完