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.
9 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}'),
);
},
)

- 3,793
- 1
- 25
- 37

- 256,336
- 79
- 519
- 432
-
2Ohh I thought itemCount is required, thank you for the clarification. – fadisdh Mar 07 '18 at 22:32
-
1Thanks Remi, I was searching for a solution to stop the infinite scroll of my pageview. – Shyju M May 04 '18 at 05:16
-
12But isn't this a hack? Ok, maybe noone will scroll 999 pages to the left, but IF he does: there is an end. So it's more like a "half-line" as far as I know. Still, good to know there is a way to give an initial page to start with. – Dominikus K. Jun 03 '18 at 21:22
-
4will this continuously eat up memory as the pages go on? or will previous pages be released from memory? – MobileMon Sep 15 '20 at 14:26
-
2@MobileMon they will be released from memory unless you specifically tell the framework to keep them. – Rémi Rousselet Sep 15 '20 at 16:06
-
The initial page starts at 999, it's not how many pages exist just where to start. Meaning that you can go all the way to 80000+ without any issue. @DominikusK. – Matthew Oct 13 '20 at 18:59
-
That is the direction I am with you @Matthew. But what with users scrolling back to the pages 998, 997, .., 1, 0, -1?? – Dominikus K. Oct 20 '20 at 19:26
-
This solution allows scrolling only in one direction, but If for example I have 3 images and start from image 1 I want to scroll backwards and get to image 3 instantly – kashlo Jan 27 '21 at 01:12
-
@RémiRousselet I want to set `initialPage` to 1. how can I do it? – BIS Tech Jun 13 '21 at 11:19
-
The builder is called only for those children that are actually visible. So it's efficient – zex_rectooor Jul 19 '22 at 09:26
-
This hack has an effect on performance, you can't set any value for initialPage, for example if you replace 999 with 999999999999999 the app becomes unusable. – Patrick Aug 23 '22 at 13:14
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,
);

- 219
- 2
- 4
-
Wondering how this is on memory. Will it continuously create memory and never release it? – MobileMon Sep 11 '20 at 11:02
-
1this won't work, cause pageController won't go lower than zero without any hacks. – A. Tratseuski Feb 08 '21 at 13:20
-
It did work for me. It's not the best way to go about it, but it works :)) – Mad World Apr 16 '22 at 19:11
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];
);
},
)

- 79
- 1
- 2
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];
},
)

- 121
- 1
- 5
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),),),
),
],
)
);
}
}

- 74
- 2
-
2
-
Did you run the code.and I think init state will run only first time when you initialize the state.so no way init state will be called two time per swipe build will be called and it going to call it only one time – Avinash katariya Jul 05 '19 at 10:45
-
did you run your code? I don't think so, because in this case you should know setState executes twice every time when you swipe to last or first page moreover if you try to swipe fast you will get "lag" and no swipe every time when last or first page is on screen – Jul 05 '19 at 12:39
-
It's a little ugly, but I think it's a good idea. I will try to catch the touch-end event and trigger the `s.jumpTo` – mutoe Jun 25 '20 at 10:53
It'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] ?? '',
);
},
)

- 523
- 5
- 11
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)
},
);
}
}

- 692
- 7
- 26
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,
),
)
],
);
}
}

- 1
- 1
final controller = new PageController(initialPage: children.length*999);
PageView.builder(
controller: controller,
itemBuilder: (context, index) {
return new Center(
child: new Text('${index % children.length}'),
);
},
)

- 162
- 4
- 6