转载于 https://juejin.im/post/5d6c973ae51d45620064bb92
通过组合
PageView
方式,实现一个自定义的infinity_slider
小部件,完整代码 https://github.com/herghost000/flutter_infinity_slider
新建infinity_slider.dart文件,添加下方代码
import 'package:flutter/material.dart';
class InfinitySlider extends StatefulWidget {
@override
_InfinitySliderState createState() => _InfinitySliderState();
}
class _InfinitySliderState extends State {
@override
Widget build(BuildContext context) {
return Container();
}
}
PageView
小部件是没有设置可无限滚动属性的items
PageView.builder
有个itemCount
字段,当我们构建PageView
时,如果没有设置itemCount
的话则PageView
的itemBuilder
会从0-(PageController.initialPage)-infinity
构建滑块,这时items会有溢出的危险。items
,我们可以通过一个函数计算出当前的items真实下标,这时items就可以向右进行无限滑动而不用担心items下标的溢出,计算函数代码如下int _calcIndex(int input, int source) {
final int result = input % source;
return result < 0 ? source + result : result;
}
/**
* _getRealIndex
*
* @param {int} position - 真实下标
* @param {int} base - 初始真实下标
* @return {int} length 滑块列表长度
*
* @since 1.0.0
*/
int _getRealIndex(int position, int base, int length) {
final int offset = position - base;
return _calcIndex(offset, length);
}
import 'package:flutter/material.dart';
import 'dart:async';
int kRealPage = 100;
int _calcIndex(int input, int source) {
final int result = input % source;
return result < 0 ? source + result : result;
}
int _getRealIndex(int position, int base, int length) {
final int offset = position - base;
return _calcIndex(offset, length);
}
typedef void UpdatePageCallback(int index);
class InfinitySlider extends StatefulWidget {
final int initialPage;
final List items;
final double height;
final PageController pageController;
InfinitySlider({
Key key,
@required this.items,
this.height: 120,
this.initialPage: 0,
}) : pageController = new PageController(
initialPage: kRealPage + initialPage,
),
assert(items != null),
assert(items.length > 0),
assert(initialPage != null),
super(key: key);
@override
_InfinitySliderState createState() => _InfinitySliderState();
}
class _InfinitySliderState extends State {
@override
Widget build(BuildContext context) {
return Container(
height: widget.height,
child: PageView.builder(
itemBuilder: (BuildContext context, int i) {
final int index = _getRealIndex(i, kRealPage, widget.items.length);
return widget.items[index];
},
controller: widget.pageController,
),
);
}
}
kRealPage
被我们设置为100,假如遇到一个无聊的人往左滑动了100次,这时就不能再次往左侧滑动import 'package:flutter/material.dart';
import 'dart:async';
int kRealPage = 100;
int _calcIndex(int input, int source) {
final int result = input % source;
return result < 0 ? source + result : result;
}
int _getRealIndex(int position, int base, int length) {
final int offset = position - base;
return _calcIndex(offset, length);
}
typedef void UpdatePageCallback(int index);
class InfinitySlider extends StatefulWidget {
final int initialPage;
final List items;
final double height;
final PageController pageController;
InfinitySlider({
Key key,
@required this.items,
this.height: 120,
this.initialPage: 0,
}) : pageController = new PageController(
initialPage: kRealPage + initialPage,
),
assert(items != null),
assert(items.length > 0),
assert(initialPage != null),
super(key: key);
@override
_InfinitySliderState createState() => _InfinitySliderState();
}
class _InfinitySliderState extends State {
@override
void initState() {
super.initState();
widget.pageController.addListener(() {
if (widget.pageController.page == (kRealPage - widget.items.length) ||
widget.pageController.page == (kRealPage + widget.items.length)) {
widget.pageController.position
.setPixels(MediaQuery.of(context).size.width * kRealPage);
}
});
}
@override
Widget build(BuildContext context) {
return Container(
height: widget.height,
child: PageView.builder(
itemBuilder: (BuildContext context, int i) {
final int index = _getRealIndex(i, kRealPage, widget.items.length);
return widget.items[index];
},
controller: widget.pageController,
),
);
}
}
import 'package:flutter/material.dart';
import 'dart:async';
int kRealPage;
double kAutoMaxPage;
double kAutoMinPage;
int _calcIndex(int input, int source) {
final int result = input % source;
return result < 0 ? source + result : result;
}
int _getRealIndex(int position, int base, int length) {
final int offset = position - base;
return _calcIndex(offset, length);
}
typedef void UpdatePageCallback(int index);
class InfinitySlider extends StatefulWidget {
final int initialPage;
final List items;
final bool autoPlay;
final Duration interval;
final Curve transCurve;
final Duration transDuration;
final double height;
final PageController pageController;
final UpdatePageCallback updateCallback;
InfinitySlider({
Key key,
@required this.items,
this.height: 120,
this.initialPage: 0,
this.autoPlay: true,
this.interval: const Duration(seconds: 2),
this.transDuration: const Duration(milliseconds: 800),
this.transCurve: Curves.fastOutSlowIn,
this.updateCallback,
}) : pageController = new PageController(
initialPage: kRealPage + initialPage,
),
assert(items != null),
assert(items.length > 0),
assert(initialPage != null),
assert(autoPlay != null),
assert(interval != null),
assert(transDuration != null),
assert(transCurve != null),
super(key: key) {
kRealPage = items.length;
kAutoMinPage = 0.0;
kAutoMaxPage = (items.length * 2).toDouble();
}
@override
_InfinitySliderState createState() => _InfinitySliderState();
Future nextPage({Duration duration, Curve curve}) {
return pageController.nextPage(duration: duration, curve: curve);
}
Future previousPage({Duration duration, Curve curve}) {
return pageController.previousPage(duration: duration, curve: curve);
}
}
class _InfinitySliderState extends State {
int currentPage;
Timer timer;
@override
void initState() {
super.initState();
widget.pageController.addListener(() {
if (widget.pageController.page == kAutoMinPage ||
widget.pageController.page == kAutoMaxPage) {
widget.pageController.position
.setPixels(MediaQuery.of(context).size.width * kRealPage);
}
});
currentPage = widget.initialPage;
if (widget.autoPlay) {
timer = new Timer.periodic(widget.interval, (_) {
widget.nextPage(
duration: widget.transDuration, curve: widget.transCurve);
});
}
}
@override
Widget build(BuildContext context) {
return Container(
height: widget.height,
child: PageView.builder(
itemBuilder: (BuildContext context, int i) {
final int index = _getRealIndex(i, kRealPage, widget.items.length);
return widget.items[index];
},
controller: widget.pageController,
onPageChanged: (int index) {
currentPage = _getRealIndex(index, kRealPage, widget.items.length);
if (widget.updateCallback != null) widget.updateCallback(currentPage);
},
),
);
}
@override
void dispose() {
super.dispose();
timer?.cancel();
}
}
InfinitySlider(
items: [1,2,3,4,5].map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
color: Colors.amber
child: new Text('text $i', style: new TextStyle(fontSize: 16.0),)
);
},
);
}).toList(),
)