默认pageview通过builder实现无限滚动时,无法让状态保持,故而手动实现了无限滚动
import 'package:flutter/cupertino.dart'
import 'package:flutter/material.dart'
class InfinityPageView extends StatefulWidget {
InfinityPageView({this.pages})
final List<Widget> pages
@override
createState() {
return _InfinityPageView(pages: pages)
}
}
class _InfinityPageView extends State with SingleTickerProviderStateMixin {
_InfinityPageView({this.pages})
final List<Widget> pages
double _position = 0
double _panEndPosition = 0
double _panStartPosition = 0
double _animateToPosition = 0
double _basePosition = 0
AnimationController _controller
@override
void initState() {
_controller = new AnimationController(
duration: const Duration(milliseconds: 200), vsync: this)
_controller.addListener(() {
double percent = _controller.value
double newPosition =
percent * (_animateToPosition) + _panEndPosition * (1 - percent)
setState(() {
_position = newPosition
})
})
super.initState()
}
@override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size
int i = -1
double leftPercent = (-_position / screenSize.width)
int ceilIndex = leftPercent.ceil()
int floorIndex = leftPercent.floor()
ceilIndex = ceilIndex % pages.length
floorIndex = floorIndex % pages.length
return GestureDetector(
onHorizontalDragUpdate: (details) {
setState(() {
_position = _position + details.delta.dx
})
},
onHorizontalDragStart: (details) {
_panStartPosition = _position
},
onHorizontalDragEnd: (details) {
//速度
final v = details.velocity.pixelsPerSecond.dx
_panEndPosition = _position
// 位移距离
final positionDiff = _panEndPosition - _panStartPosition
final minDiff = screenSize.width / 2
// 动画执行到的索引
final newPositionIndex = (_panStartPosition / screenSize.width).round()
// 初始页码位置
_basePosition = newPositionIndex * screenSize.width
// 位移超过半页或则速度大于两百,则认为翻页
if (positionDiff > minDiff || v > 200) {
_animateToPosition = _basePosition + screenSize.width
} else if (positionDiff < -minDiff || v < -200) {
_animateToPosition = _basePosition - screenSize.width
} else {
_animateToPosition = _basePosition
}
_controller.forward(from: 0)
},
child: Stack(
children: [
...pages.map((page) {
i++; //0,1,2,3....
double left;
/// 设屏幕宽度为100,当前位置为x(x为left的值,无区间限制),pages数组长度为3
/// 当x为屏幕宽度的倍数(-100,0,100..),只需要展示对应的page
/// 否则:先计算出x跨度的两个page,根据x的值展示到对应位置
/// 跨度计算:若x为50,则跨度页面的索引分别为2、0
/// 跨度位置计算:
/// x=1,pos{2:100-1,0:1}
/// x=-1,pos{0:-1,1:100-1}
// 计算当前position的page index, position除以屏幕宽度得到整,再取正整数余数
left = screenSize.width;
double usefulPosition = _position % left;
if (floorIndex == ceilIndex && ceilIndex == i) {
left = 0;
} else {
if (usefulPosition > 0) {
if (i == floorIndex) {
left = usefulPosition - screenSize.width;
} else if (i == ceilIndex) {
left = usefulPosition;
}
} else {
if (i == floorIndex) {
left = usefulPosition;
} else if (i == ceilIndex) {
left = screenSize.width + usefulPosition;
}
}
}
return Positioned(
left: left,
top: 0,
width: screenSize.width,
height: screenSize.height,
child: Container(
color: Color.fromARGB(0, 0, 0, 0),
child: page,
),
);
}),
],
),
)
}
}