Is there a way to have an infinite loop using PageView in Flutter? For example if my PageView has 5 pages, after swiping to page 5, I would be able to swipe again in the same direction to get to Page 1.
11 Answers
By default, PageView.builder is infinite in flutter. Unless you provide an itemCount.
The following will print page from 0 to 4 infinitely
final controller = new PageController(initialPage: 999);
...
new PageView.builder(
controller: controller,
itemBuilder: (context, index) {
return new Center(
child: new Text('${index % 5}'),
);
},
)
11 Comments
If you have a list of pre-defined Widgets, you can achieve continuous scrolling using:
return PageView.builder(
itemBuilder: (context, index) {
return _children[index % _children.length];
},
controller: pageController,
);
3 Comments
I've found a good solution using this lib https://pub.dev/packages/infinity_page_view
Just import the lib and use InfinityPageView instead of PageView
InfinityPageView(
controller: infinityPageController,
itemCount: colorList.length,
itemBuilder: (context, index) {
return Container(
color: colorList[index];
);
},
)
Comments
You can achieve infinite scrolling using PageView builder, without giving value to itemCount there will be infinite pages, you just have to maintain the page index which will be painted on screen.
PageView.builder(
controller: _pageController,
scrollDirection: Axis.horizontal,
onPageChanged: (index) {
setState(() {
_currentIndex = index % _walkthroughSlides.length;
});
},
itemBuilder: (context, index) {
return _walkthroughSlides[index % _walkthroughSlides.length];
},
)
2 Comments
Used extention beside remi solution
extension ListX<E> on List {
E loop(int index) => this[index % length];
}
class _CarouselWidgetState extends State<CarouselWidget> {
late final List<Widget> children;
late final PageController controller;
@override
initState() {
super.initState();
children = widget.children;
controller = PageController(initialPage: 100);
}
@override
Widget build(BuildContext context) {
return PageView.builder(
controller: controller,
// itemCount: children.length,
itemBuilder: (context, pagePosition) {
return children.loop(pagePosition)
},
);
}
}
Comments
You can use an "OverScroll notifier" and PageController controller.jumpToPage() to change the current page to the last or the first page depending on the edge we "overscrolled".
class CarousselView extends StatefulWidget {
const CarousselView({super.key});
@override
State<CarousselView> createState() => _CarousselViewState();
}
class _CarousselViewState extends State<CarousselView>
with SingleTickerProviderStateMixin {
int _index = 1;
TabController? controller;
late PageController _controller;
String message = "";
@override
void initState() {
controller = TabController(
length: 6,
vsync: this,
);
_controller = PageController();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
NotificationListener<OverscrollIndicatorNotification>(
onNotification: ((notification) {
print("Oversrolled");
setState(() {
_controller.jumpToPage(_index == 0 ? 5 : 0);
});
return true;
}),
child: PageView.builder(
scrollDirection: Axis.horizontal,
itemCount: 6,
controller: _controller,
onPageChanged: (int index) => setState(() {
controller!.index = index;
_index = index;
}),
itemBuilder: (_, i) {
return Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Center(
child: Text(
"Card ${i}",
style: TextStyle(fontSize: 32),
),
),
);
},
),
),
Container(
alignment: Alignment.bottomCenter,
child: TabPageSelector(
indicatorSize: 6.0,
controller: controller,
),
)
],
);
}
}
2 Comments
The answer mentioned above will take more memory and probably that is redundancy and when you are on the first child you can't go the last one by scrolling in reverse direction
import 'package:flutter/material.dart';
import 'dart:async';
class InfiniteScroll extends StatefulWidget{
final List<Widget> children;
InfiniteScroll({this.children});
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _InfiniteScrollState();
}
}
class _InfiniteScrollState extends State<InfiniteScroll>{
List<Widget> children;
ScrollController s;
int _pos;
@override
void initState() {
_pos=1;
children = [widget.children[widget.children.length - 1]];
for (int i = 0; i < widget.children.length; ++i) {
children.add(widget.children[i]);
}
if (widget.children.length > 1) {
children.add(children[1]);
}
s = PageController();
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_){
s.jumpTo(MediaQuery.of(context).size.width);
});
}
@override
Widget build(BuildContext context) {
return PageView.builder(
scrollDirection: Axis.horizontal,
controller: s,
onPageChanged: (int i) {
setState(() {
_pos=i+1;
});
if (i == children.length - 1) {
Timer(Duration(milliseconds: 400), () {
s.jumpTo(MediaQuery.of(context).size.width);
});
setState(() {
_pos=1;
});
} else if (i == 0) {
Timer(Duration(milliseconds: 400), () {
s.jumpTo(MediaQuery.of(context).size.width * (children.length - 2));
});
setState(() {
_pos=children.length-2;
});
}
},
itemBuilder: (BuildContext context, int index) {
return children[index];
},
itemCount: children.length,
);
}
}
and now you can use it as
import 'package:flutter/material.dart';
import 'package:test1/Widgets/infinite_scroll.dart';
class CoursesPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return
Container(
height: MediaQuery.of(context).size.height,
child:
InfiniteScroll(
children: <Widget>[
Container(
color: Colors.red,
child: Center(child: Text("1",style: TextStyle(color: Colors.white,fontSize: 50),),),
),
Container(
color: Colors.black,
child: Center(child: Text("2",style: TextStyle(color: Colors.white,fontSize: 50),),),
),
Container(
color: Colors.green,
child: Center(child: Text("3",style: TextStyle(color: Colors.white,fontSize: 50),),),
),
Container(
color: Colors.blue,
child: Center(child: Text("4",style: TextStyle(color: Colors.white,fontSize: 50),),),
),
],
)
);
}
}
4 Comments
s.jumpToIt's work without controller. You need comment prop itemCount & repce index like bellow.
PageView.builder(
onPageChanged: (v) {
setState(() => _currentImage = v % images.length);
},
scrollDirection: Axis.horizontal,
// itemCount: images.length,
itemBuilder: (ctx, i) {
return CustomImageNetwork(
width: imageSize,
height: imageSize,
image: images[i % images.length] ?? '',
);
},
)
Comments
You should try out the loop page view https://pub.dev/packages/loop_page_view it's a pretty straightforward plugin
import 'package:flutter/material.dart';
import 'package:loop_page_view/loop_page_view.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Loop Page View Demo'),
),
body: Center(
child: LoopPageView.builder(
itemCount: 2,
itemBuilder: (_, index) {
return Card(
child: Center(
child: Text('$index'),
),
);
},
),
),
),
);
}
}
Comments
Just Simple Alternative
In my case, i am using infinite builder by default. no itemCount. and my list is widget.images .
PageView.builder(
controller: _pageController,
allowImplicitScrolling: true,
itemBuilder: (context, index) {
final url = widget.images[index % widget.images.length];
return InteractiveViewer(maxScale: 3, child: Image.asset(url));
},
),
But also, i want to view initial page, so i manage using _pageController . To initialize _pageController , i sent initialIndex for example index 2.
When i snap to right, infinite looping work. after i reach last page, the page show first page again and keep going.
But !!, when i star over again. after pageview show initial page then i snap to left, infinite loop not work. from this i learn that is pageview default infinite only work for upcoming index.
To fix this, i create schema to handle range page/index e.g. from 0-1000. and create function to calculateInitialIndexRepresentation. this index will be center of range page. if range 0-1000 and listCount is 10, i create _infiniteScrollFactor as a multiplier then initialIndex will be 500 as center. This will work to infinite loop for left direction until indexValue is 0.
and here to initialize _pageCntroller and calculate initial:
late final PageController _pageController;
// index/page
// min: 0, max: 100 * widget.images.length
static const int _infiniteScrollFactor = 100;
int _calculateInitialPage() {
return (_infiniteScrollFactor ~/ 2) * widget.images.length +
widget.initialIndex;
}
