黄沙百战穿金甲
不破楼兰终不还
前言
通过封装PageView+Timer实现无限轮播,手动拖拽时停止定时器功能,拖拽完成后开启定时器。
功能
- 自动轮播
- 手动轮播
- 指示器
功能展示
代码实现
轮播组件的构造方法
class Carousel extends StatefulWidget {
final List 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 createState() {
return _BannerState();
}
}
组件状态实现方法
class _BannerState extends State {
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 _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: [
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: [
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为可选参数,
}