This practical article walks you through a couple of different examples of implementing the PageView
widget in Flutter. The first example is simple and straightforward, while the second one is a little bit complex because it goes with a dot indicator. Before writing code, let’s explore the fundamentals.
The Fundamentals
According to the official docs, the PageView widget is used to create a scrollable list that works page by page. One of the most common use cases is carousel/slideshow images which you can come across in many places on the web or in mobile apps.
You can construct a PageView
from an explicit list of widgets by using this constructor:
PageView({
Key? key,
Axis scrollDirection = Axis.horizontal,
bool reverse = false,
PageController? controller,
ScrollPhysics? physics,
bool pageSnapping = true,
ValueChanged<int>? onPageChanged,
List<Widget> children = const <Widget>[],
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
bool allowImplicitScrolling = false,
String? restorationId,
Clip clipBehavior = Clip.hardEdge,
ScrollBehavior? scrollBehavior,
bool padEnds = true
})
If you want to implement a PageView
that uses widgets that are created on demand (e.g., dynamic items loaded from a remote server), use this constructor:
PageView.builder({
Key? key,
Axis scrollDirection = Axis.horizontal,
bool reverse = false,
PageController? controller,
ScrollPhysics? physics,
bool pageSnapping = true,
ValueChanged<int>? onPageChanged,
required NullableIndexedWidgetBuilder itemBuilder,
ChildIndexGetter? findChildIndexCallback,
int? itemCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
bool allowImplicitScrolling = false,
String? restorationId,
Clip clipBehavior = Clip.hardEdge,
ScrollBehavior? scrollBehavior,
bool padEnds = true
})
To get a better understanding, let’s examine the upcoming examples
Minimum Viable Example
Preview
The Code
The full source code in main.dart
with explanations:
// kindacode.com
// PageView example
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// remove the debug banner
debugShowCheckedModeBanner: false,
title: 'KindaCode.com',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const KindaCode());
}
}
class KindaCode extends StatefulWidget {
const KindaCode({Key? key}) : super(key: key);
@override
State<KindaCode> createState() => _KindaCodeState();
}
class _KindaCodeState extends State<KindaCode> {
// Declare and Initialize the PageController
final PageController _pageController =
PageController(initialPage: 0, viewportFraction: 1);
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
children: const [PageOne(), PageTwo(), PageThree()],
),
);
}
// Dispose the PageController
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
}
// Page One
class PageOne extends StatelessWidget {
const PageOne({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
color: Colors.indigo,
child: const Text(
'Page 1',
style: TextStyle(fontSize: 50, color: Colors.white),
),
);
}
}
// Page Two
class PageTwo extends StatelessWidget {
const PageTwo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
color: Colors.green,
child: const Text(
'Page 2',
style: TextStyle(fontSize: 50, color: Colors.white),
));
}
}
// Page Three
class PageThree extends StatelessWidget {
const PageThree({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
color: Colors.amber,
child: const Text(
'Page 3',
style: TextStyle(fontSize: 50, color: Colors.black),
));
}
}
PageView with Dots Indicator
Preview
This example is an enhancement of the preceding one. It displays a PageView
with a dot indicator. Each page is associated with a dot. The dot corresponding to the current page will have a different color than the other dots. Furthermore, users can move between pages by swiping left, right, or tapping on a dot. Here’s how it works in action:
The Code
The complete code in main.dart
with explanations in the comments:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// remove the debug banner
debugShowCheckedModeBanner: false,
title: 'KindaCode.com',
theme: ThemeData(
useMaterial3: true,
),
home: const KindaCodeDemo());
}
}
// Construct Dots Indicator
class KindaCodeDemo extends StatefulWidget {
const KindaCodeDemo({Key? key}) : super(key: key);
@override
State<KindaCodeDemo> createState() => _KindaCodeDemoState();
}
class _KindaCodeDemoState extends State<KindaCodeDemo> {
// declare and initizlize the page controller
final PageController _pageController = PageController(initialPage: 0);
// the index of the current page
int _activePage = 0;
// this list holds all the pages
// all of them are constructed in the very end of this file for readability
final List<Widget> _pages = [
const PageOne(),
const PageTwo(),
const PageThree()
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// the page view
PageView.builder(
controller: _pageController,
onPageChanged: (int page) {
setState(() {
_activePage = page;
});
},
itemCount: _pages.length,
itemBuilder: (BuildContext context, int index) {
return _pages[index % _pages.length];
},
),
// Display the dots indicator
Positioned(
bottom: 0,
left: 0,
right: 0,
height: 100,
child: Container(
color: Colors.black54,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(
_pages.length,
(index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: InkWell(
onTap: () {
_pageController.animateToPage(index,
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn);
},
child: CircleAvatar(
radius: 8,
// check if a dot is connected to the current page
// if true, give it a different color
backgroundColor: _activePage == index
? Colors.amber
: Colors.grey,
),
),
)),
),
),
),
],
),
);
}
}
// Page One
class PageOne extends StatelessWidget {
const PageOne({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
color: Colors.pink,
child: const Text(
'KindaCode.com',
style: TextStyle(fontSize: 30, color: Colors.white),
),
);
}
}
// Page Two
class PageTwo extends StatelessWidget {
const PageTwo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
color: Colors.green,
child: const Text(
'Green Page',
style: TextStyle(fontSize: 50, color: Colors.white),
));
}
}
// Page Three
class PageThree extends StatelessWidget {
const PageThree({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: const Text(
'Blue Page',
style: TextStyle(fontSize: 50, color: Colors.white),
));
}
}
Afterword
We’ve walked through a few examples of the PageView
widget in Flutter to create nice and interactive user interfaces. From this time forward, you’ll have a solid knowledge to harness in many situations related to lists of widgets that work page by page. Continue learning and strengthening your skills by taking a look at the following articles:
- Flutter & VS Code: Auto Trigger Hot Reload on Save
- How to Parse and Render XML Data in Flutter
- Flutter: Best Packages to Create Cool Bottom App Bars
- Flutter: Giving a Size to a CircularProgressIndicator
- Flutter: Get Current Scroll Offset in ListView/GridView
- How to make an image carousel in Flutter
You can also tour around our Flutter topic page or Dart topic page for the most recent tutorials and examples.
Can we use key name to indicate page instead of number key[index] ?
Yes