31_Flutter之从无到有撸一个轮播图组件
Flutter之从无到有撸一个轮播图组件
本文将基于PageView组件实现一个轮播图组件,PageView组件和android中的ViewPage类似
一.轮播图组件应该具有如下属性
-
scrollDirection:滑动或轮播方向
- Axis.horizontal: 左右轮播
- Axis.vertical: 上下轮播
-
children: 轮播图的各个item,类型为List
-
indicatorSize: 指示器的大小,类型为Size
-
indicatorSelectedColor: 指示器在选中状态下的颜色,类型为Color
-
indicatorUnselectedColor: 指示器在未选中状态下的颜色,类型为Color
-
indicatorRadius: 指示器的圆角半径,类型为double
-
indicatorBackgroundColor: 摆放指示器的父组件的背景色,类型为Color
-
indicatorPosition: 指示器显示的位置,类型为:
enum { left, right, top, bottom }
-
indicatorPadding: 摆放指示器的父组件的padding,类型为EdgeInsetsGeometry
-
indicatorAlignment: 由于摆放指示器的父组件是一个Row组件,所以这里为指示器在主轴方向的对齐方式,类型为MainAxisAlignment
- MainAxisAlignment.start: 开始位置对齐
- MainAxisAlignment.center: 居中对齐
- MainAxisAlignment.end: 结束位置对齐
-
duration: 轮播时长,类型为int
-
animationDuration: 轮播图的item切换动画的时长,类型为int
-
showIndicator: 是否显示指示器,类型为bool
二. scrollDirection
由于要根据PageView的选中状态来更新指示器的选中状态,所以该轮播图组件应该为一个有状态组件,即应该继承自StatefulWidget
class CarouselView extends StatefulWidget {
final Axis scrollDirection;
CarouselView({
Key key,
this.scrollDirection = Axis.horizontal,
}):super(key:key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _CarouselViewState();
}
}
class _CarouselViewState extends State<CarouselView> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Stack(
children: <Widget>[
PageView(
scrollDirection: widget.scrollDirection,
)
],
);
}
}
三.children
class CarouselView extends StatefulWidget {
final Axis scrollDirection;
final List<Widget> children;
CarouselView({
Key key,
this.scrollDirection = Axis.horizontal,
this.children,
}):super(key:key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _CarouselViewState();
}
}
class _CarouselViewState extends State<CarouselView> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Stack(
children: <Widget>[
PageView(
scrollDirection: widget.scrollDirection,
children: widget.children,
)
],
);
}
}
为CarrouselView设置children,就可以将item显示在CarrouselView中了:
home: Scaffold(
appBar: AppBar(
title: Text("CarouselView"),
),
body: Container(
child: Container(
width: double.infinity,
height: 240,
child: CarouselView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Image.network("http://pic23.nipic.com/20120727/10614465_163109621190_2.jpg", fit: BoxFit.cover),
Image.network("http://pic27.nipic.com/20130311/11468523_154151382161_2.jpg", fit: BoxFit.cover),
Image.network("http://pic59.nipic.com/file/20150203/2306733_113254806000_2.jpg", fit: BoxFit.cover),
],
),
)
),
)
四.添加指示器,并实现选中状态切换
class CarouselView extends StatefulWidget {
final Axis scrollDirection;
final List<Widget> children;
final Size indicatorSize;
final Color indicatorSelectedColor;
final Color indicatorUnselectedColor;
final double indicatorRadius;
final IndicatorPosition indicatorPosition;
final EdgeInsetsGeometry indicatorPadding;
final MainAxisAlignment indicatorAlignment;
_Position _position;
CarouselView({
Key key,
this.scrollDirection = Axis.horizontal,
this.children,
this.indicatorSize,
this.indicatorSelectedColor,
this.indicatorUnselectedColor,
this.indicatorRadius,
this.indicatorPosition = IndicatorPosition.bottom,
this.indicatorPadding,
this.indicatorAlignment = MainAxisAlignment.start,
}):super(key:key) {
_position = _judgePosition();
}
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _CarouselViewState();
}
/**
* 判断指示器的显示位置
*/
_Position _judgePosition() {
_Position position = _Position();
switch(indicatorPosition) {
case IndicatorPosition.left:
position = _Position(left: 0, right: null, top: 0, bottom: 0);
break;
case IndicatorPosition.right:
position = _Position(left:null, right: 0, top: 0, bottom: 0);
break;
case IndicatorPosition.top:
position = _Position(left: 0, right:0, top: 0, bottom: null);
break;
case IndicatorPosition.bottom:
position = _Position(left: 0, right:0, top: null, bottom: 0);
break;
}
return position;
}
}
class _CarouselViewState extends State<CarouselView> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
// TODO: implement build
return Stack(
children: <Widget>[
PageView(
scrollDirection: widget.scrollDirection,
children: widget.children,
onPageChanged: (index) {
/**
* 通知Flutter去重新渲染界面,从而改变指示器的选中状态
*/
_changeCurrentIndex(index);
}
),
Positioned(
left: widget._position.left,
right: widget._position.right,
top: widget._position.top,
bottom: widget._position.bottom,
child: Container(
// color: Color(0xffff0000),
padding: widget.indicatorPadding,
child: Row(
mainAxisAlignment: widget.indicatorAlignment,
children: _buildIndicators()
),
),
)
],
);
}
/**
* 生成指示器
*/
List<Widget> _buildIndicators() {
List<Widget> indicators = <Widget>[];
for(int i=0; i<widget.children.length; i++) {
indicators.add(
Container(
width: widget.indicatorSize.width,
height: widget.indicatorSize.height,
decoration: BoxDecoration(
color: _currentIndex == i ? widget.indicatorSelectedColor : widget.indicatorUnselectedColor,
borderRadius: BorderRadius.all(Radius.circular(widget.indicatorRadius))
),
margin: i > 0 || i< widget.children.length - 1 ? EdgeInsets.symmetric(horizontal: 3) : i == 0 ? EdgeInsets.only(right: 3):EdgeInsets.only(left: 3),
)
);
}
return indicators;
}
void _changeCurrentIndex(int index) {
setState(() {
_currentIndex = index;
print(_currentIndex);
});
}
}
class _Position {
final double left;
final double right;
final double top;
final double bottom;
_Position({
this.left,
this.right,
this.top,
this.bottom,
});
}
enum IndicatorPosition {
left,
right,
top,
bottom
}
home: Scaffold(
appBar: AppBar(
title: Text("CarouselView"),
),
body: Container(
child: Container(
width: double.infinity,
height: 240,
child: CarouselView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Image.network("http://pic23.nipic.com/20120727/10614465_163109621190_2.jpg", fit: BoxFit.cover),
Image.network("http://pic27.nipic.com/20130311/11468523_154151382161_2.jpg", fit: BoxFit.cover),
Image.network("http://pic59.nipic.com/file/20150203/2306733_113254806000_2.jpg", fit: BoxFit.cover),
],
indicatorSize: Size(10, 10),
indicatorSelectedColor: Color(0xff0000ff),
indicatorUnselectedColor: Color(0xff00ff00),
indicatorRadius: 5,
indicatorAlignment: MainAxisAlignment.center,
indicatorPosition: IndicatorPosition.bottom,
indicatorPadding: EdgeInsets.all(10.0),
),
)
),
)
五.item循环滚动实现
-
children = [A, B, C]
-
initialPage = 1
-
当向左滑动到index = 0时,应重新切换到对应children的index=2
-
当向右滑动到index = 4时,应重新切换到对应children的index=0
class _CarouselViewState extends State<CarouselView> { ... static PageController _controller = PageController( initialPage: 1 ); static int _oldIndex = 1; @override Widget build(BuildContext context) { // TODO: implement build return Stack( children: <Widget>[ PageView( scrollDirection: widget.scrollDirection, children: _buildPages(), controller: _controller, onPageChanged: (index) { _oldIndex = index; if(_oldIndex == 0) { index = widget.children.length - 1; Future.delayed(Duration(milliseconds: 500), (){ _controller.jumpToPage(widget.children.length); }); } else if(_oldIndex == widget.children.length + 1) { index = 0; Future.delayed(Duration(milliseconds: 500), (){ _controller.jumpToPage(1); }); } else { index -= 1; } _changeCurrentIndex(index); }, ), ... ], ); } List<Widget> _buildPages() { List<Widget> pages = <Widget> []; int firstIndex = 0; int lastIndex = widget.children.length - 1; pages.add(widget.children[lastIndex]); for(int i=0; i<widget.children.length; i++) { pages.add(widget.children[i]); } pages.add(widget.children[firstIndex]); return pages; } ... }
六.上一页和下一页实现
class CarouselView extends StatefulWidget {
...
final int animationDuration;
CarouselView({
...
this.animationDuration = 500,
}):super(key: key) {
_position = _judgePosition();
}
static void prevPage() {
_CarouselViewState._prevPage();
}
static void nextPage() {
_CarouselViewState._nextPage();
}
}
class _CarouselViewState extends State<CarouselView> {
int _currentIndex = 0;
static PageController _controller = PageController(
initialPage: 1
);
static int _oldIndex = 1;
static int _pageSize;
static int _animationDuration;
...
List<Widget> _buildPages() {
List<Widget> pages = <Widget> [];
int firstIndex = 0;
int lastIndex = widget.children.length - 1;
pages.add(widget.children[lastIndex]);
for(int i=0; i<widget.children.length; i++) {
pages.add(widget.children[i]);
}
pages.add(widget.children[firstIndex]);
_pageSize = widget.children.length;
_animationDuration = widget.animationDuration;
return pages;
}
static void _prevPage() {
int index = _oldIndex - 1;
_controller.animateToPage(index, duration: Duration(milliseconds: _animationDuration), curve: Curves.linear);
}
static void _nextPage() {
int index = _oldIndex + 1;
_controller.animateToPage(index, duration: Duration(milliseconds: _animationDuration), curve: Curves.linear);
}
}
home: Scaffold(
appBar: AppBar(
title: Text("CarouselView"),
),
body: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children:<Widget>[
Container(
width: double.infinity,
height: 240,
child: CarouselView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Image.network("http://pic23.nipic.com/20120727/10614465_163109621190_2.jpg", fit: BoxFit.cover),
Image.network("http://pic27.nipic.com/20130311/11468523_154151382161_2.jpg", fit: BoxFit.cover),
Image.network("http://pic59.nipic.com/file/20150203/2306733_113254806000_2.jpg", fit: BoxFit.cover),
],
indicatorSize: Size(10, 10),
indicatorSelectedColor: Color(0xff0000ff),
indicatorUnselectedColor: Color(0xff00ff00),
indicatorRadius: 5,
indicatorAlignment: MainAxisAlignment.center,
indicatorPosition: IndicatorPosition.bottom,
indicatorPadding: EdgeInsets.all(10.0),
),
),
RaisedButton(
onPressed: () {
CarouselView.prevPage();
},
child: Text('上一页'),
),
RaisedButton(
onPressed: () {
CarouselView.nextPage();
},
child: Text('下一页'),
),
]
),
)
七.自动轮播实现
class CarouselView extends StatefulWidget {
...
final int duration;
_Position _position;
CarouselView({
...
this.duration = 3000,
}):super(key: key) {
_position = _judgePosition();
}
@override
State<StatefulWidget> createState() {
// TODO: implement createState
_start();
return _CarouselViewState();
}
void _start() {
Future.delayed(Duration(milliseconds: duration), (){
CarouselView.nextPage();
_start();
});
}
...
}
上一篇: TensorFlow从入门到理解(二):你的第一个神经网络
下一篇: android O音量键唤醒