how to use GetX Flutter package to create a PageView

Hi guys, I am a flutter developer and in this lecture I will show you how to use GetX package for creating a PageView inside Flutter!

first of all we create a new flutter project with this command:

flutter create pageView

then we run our project on an emulator and see this page

Image for post
Image for post

firstly I will create it with flutter StatefulWidget to see how to create PageView in flutter then I will convert this StatefulWidget to a StatelessWidget and handle state with flutter GetX package.

now let’s go!

first of all we define tow variable:

int page = 0;
PageController controller = PageController();

then we create a view with Stack and it has two child body and bottomNavBar the code is :

@override
Widget build(BuildContext context) {

var width = MediaQuery.of(context).size.width;

return new Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: Stack(
children: [
PageView(
children: <Widget>[
Home(),
Search(),
Liked(),
News()
],
controller: controller,
pageSnapping: false,
physics: NeverScrollableScrollPhysics(),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: bottomNav(width),
),
],
),
));
}

I create Home screen with this code:

import 'package:flutter/material.dart';

class Home extends StatelessWidget {

@override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
return new Container(
height: height,
alignment: Alignment.center,
child: Text('Home'),
);
}

}

it is same story about other pages.

so what is the bottomNav function?it is this code:

bottomNav(width){
return Container(
height: 60,
width: width,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
boxShadow: [boxShadow],borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
),
color: Colors.white),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
item(0,Icons.home,width),
item(1,Icons.search,width),
item(2,Icons.favorite_outline,width),
item(3,Icons.fiber_new,width)
],
));
}

and item function that is about view of each item is :

item(index,name,width) {
return Material(
color: Colors.white.withOpacity(.5),
borderRadius: BorderRadius.all(Radius.circular(9)),
child: InkWell(
splashColor: Colors.white.withOpacity(.1),
borderRadius: BorderRadius.all(Radius.circular(500)),
enableFeedback: true,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
),
),
width: width*.18,
height: 50,
child: Icon(name, color: index == page ? Colors.purple : Colors.grey,
size: index == page ? 30 : 25)
),
onTap: (){
navigateToPage(index);
},
),
);
}

now look at emulator we create this screen :

Image for post
Image for post

now if I want to chage page with tapping on each item i must call this function

navigateToPage(int input) {
page = input;
controller.animateToPage(input, duration: Duration(milliseconds: 300), curve: Curves.ease);
setState(() {});
}

now let’s make it more complex with this question

How to change page of a PageView from another page and scroll to bottom?

now first I will create another PageView inside one of the screens and the result is:

so what is the code ? the home page is :

class Home extends StatefulWidget {

@override
HomeState createState() => HomeState();
}

class HomeState extends State<Home> {

var page = 0;
var controller = PageController();
var fromController = ScrollController(initialScrollOffset: 0.0);

@override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
return new Container(
height: height,
alignment: Alignment.center,
child: profileUi(width,height),
);
}

profileUi(width, height) {
return Container(
width: width,
height: height,
color: Colors.white,
child: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
new SliverAppBar(
expandedHeight: 430,
floating: true,
pinned: true,
bottom: PreferredSize(
child: Material(
color: Colors.white,
child: tabBar(width),
),
preferredSize: Size.fromHeight(50),
),
flexibleSpace: FlexibleSpaceBar(
background: header(width,height)),
backgroundColor: Colors.white,
automaticallyImplyLeading: false,
elevation: 0,
),
];
},
controller: fromController,
body: tabBarView(width, height)));
}

header(width,height) {
return Container(
height: 450,
width: width,
color: Colors.purple,
);
}

tabBar(width) {
return Container(
width: width,
color: Colors.amber,
child: Stack(
alignment: Alignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
animateTo(0);
},
child: Container(
alignment: Alignment.center,
height: 50,
child: Text('Page 1',
style: TextStyle(color: page == 0 ? Colors.white : Colors.grey)),
width: width * .23),
),
InkWell(
onTap: () {
animateTo(1);
},
child: Container(
alignment: Alignment.center,
height: 50,
child: Text('Page2',
style: TextStyle(color: page == 1 ? Colors.white : Colors.grey)),
width: width * .23),
),
InkWell(
onTap: () {
animateTo(2);
},
child: Container(
alignment: Alignment.center,
height: 50,
child: Text('Page 3',
style: TextStyle(color: page == 2 ? Colors.white : Colors.grey)),
width: width * .23),
),
InkWell(
onTap: () {
animateTo(3);
},
child: Container(
alignment: Alignment.center,
height: 50,
child: Text('Page 4',
style: TextStyle(color: page == 3 ? Colors.white : Colors.grey)),
width: width * .23),
),
],
),
],
),
);
}

tabBarView(width, height) {
return Container(
height: height - 140,
padding: EdgeInsets.only(bottom: 65),
child: PageView(
children: <Widget>[
Container(
height: height,
alignment: Alignment.center,
child: Text('Page 1'),
),
Container(
height: height,
alignment: Alignment.center,
child: Text('Page 2'),
),
Container(
height: height,
alignment: Alignment.center,
child: Text('Page 3'),
),
Container(
height: height,
alignment: Alignment.center,
child: Text('Page 4'),
),
],
controller: controller,
onPageChanged: (value){
page = value;
setState(() {});
},
pageSnapping: true,
),
);
}

animateTo(int input) {
page = input;
controller.animateToPage(input, duration: Duration(milliseconds: 300), curve: Curves.ease);
setState(() {});
}
}

so now I want to call pageController of Home page from otherPages like new page:

first I add some buttons to news page and result is:

Image for post
Image for post

how about code? the news page code is :

import 'package:flutter/material.dart';

typedef IntCallback = Function(int num);

class News extends StatelessWidget {

final IntCallback page;
News({this.page});

@override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
return new Container(
height: height,
alignment: Alignment.center,
child: SingleChildScrollView(
child: Column(
children: [
OutlineButton(
onPressed:()=>page(0),
child: Text('go Home and Page 1'),
),
OutlineButton(
onPressed:()=>page(1),
child: Text('go Home and Page 2'),
),
OutlineButton(
onPressed:()=>page(2),
child: Text('go Home and Page 3'),
)
],
),
),
);
}

}

now in bottom page we define two parameter:

int homePage = 0;
double scroll = 0.0;

and change the PageView widget to :

PageView(
children: <Widget>[
Home(page: homePage,scroll: scroll),
Search(),
Liked(),
News(page: (val){
setState(() {
homePage = val;
scroll = 390;
});
navigateToPage(0);
},)
],
controller: controller,
pageSnapping: false,
physics: NeverScrollableScrollPhysics(),
),

and inside home page define two value :

final int page;
final double scroll;
Home({this.page,this.scroll});

and inside home page initState:

@override
void initState() {
page = widget.page ?? 0;
controller = PageController(initialPage: widget.page ?? 0);
fromController = ScrollController(initialScrollOffset: widget.scroll ?? 0.0);
super.initState();
}

now the result is:

so we handle these with flutter StatefulWidget now time to convert them to StatelessWidgets!

first we must install get package of flutter you can find it from this link:

then we add this line to our pubspec.yaml file:

get: ^3.24.0

now we have get package!

first I create a folder named controller then create two dart file named,homeCntroller and bottomController.

inside bottomController add this code:

import 'package:get/get.dart';
import 'package:flutter/material.dart';

class BottomController extends GetxController{

var page = 0.obs;
var controller = PageController().obs;

onPageChanged(input) {
page.value = input;
}

animateTo(int page) {
if (controller.value.hasClients)
controller.value.animateToPage(page,
duration: Duration(milliseconds: 300), curve: Curves.easeIn);
}
}

and inside homeController add this code:

import 'package:get/get.dart';
import 'package:flutter/material.dart';

class OpenController extends GetxController{

var page = 0.obs;
var controller = PageController().obs;
var fromController = ScrollController(initialScrollOffset: 0.0).obs;

onPageChanged(input) {
page.value = input;
}

animateTo(int page) {
if (controller.value.hasClients)
controller.value.animateToPage(page,
duration: Duration(milliseconds: 300), curve: Curves.easeIn);
}

resetController(int page) {
controller.value = PageController(initialPage: page);
}

}

now inside bottom page first we convert it to StatelessWidget then define the controllers and remove parameters and change it to some thing like this but you must remember that if a view of pag will be change by controller you must put it inside a Obx, now in bottom page:

class Bottom extends StatelessWidget {

@override
Widget build(BuildContext context) {

var width = MediaQuery.of(context).size.width;
final BottomController boController = Get.put(BottomController());
final OpenController drawer = Get.put(OpenController());

return new Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: Stack(
children: [
PageView(
children: <Widget>[
Home(),
Search(),
Liked(),
News(page: (val){
animateTo(val,drawer);
boController.animateTo(0);
boController.onPageChanged(0);
drawer.fromController.value = ScrollController(initialScrollOffset: 390);
},)
],
controller: boController.controller.value,
pageSnapping: false,
physics: NeverScrollableScrollPhysics(),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: bottomNav(width,boController,drawer),
),
],
),
));
}

bottomNav(width,boController,drawer){
return Container(
height: 60,
width: width,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
boxShadow: [boxShadow],borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
),
color: Colors.white),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
item(0,Icons.home,width,boController,drawer),
item(1,Icons.search,width,boController,drawer),
item(2,Icons.favorite_outline,width,boController,drawer),
item(3,Icons.fiber_new,width,boController,drawer)
],
));
}

navigateToPage(int input,BottomController boController,OpenController drawer) {
boController.animateTo(input);
boController.onPageChanged(input);
if(input == 0){
drawer.onPageChanged(0);
drawer.resetController(input);
}
}

item(index,name,width,boController,drawer) {
return Material(
color: Colors.white.withOpacity(.5),
borderRadius: BorderRadius.all(Radius.circular(9)),
child: InkWell(
splashColor: Colors.white.withOpacity(.1),
borderRadius: BorderRadius.all(Radius.circular(500)),
enableFeedback: true,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
),
),
width: width*.18,
height: 50,
child: Obx(()=>Icon(name, color: index == boController.page.value ? Colors.purple : Colors.grey,
size: index == boController.page.value ? 30 : 25))
),
onTap: (){
navigateToPage(index,boController,drawer);
},
),
);
}

animateTo(int page,OpenController drawer) {
drawer.onPageChanged(page);
drawer.resetController(page);
}
}

and in home page:

class Home extends StatelessWidget {

@override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;

final OpenController drawer = Get.find();


return new Container(
height: height,
alignment: Alignment.center,
child: profileUi(width,height,drawer),
);
}

profileUi(width, height,drawer) {
return Container(
width: width,
height: height,
color: Colors.white,
child: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
new SliverAppBar(
expandedHeight: 430,
floating: true,
pinned: true,
bottom: PreferredSize(
child: Material(
color: Colors.white,
child: tabBar(width,drawer),
),
preferredSize: Size.fromHeight(50),
),
flexibleSpace: FlexibleSpaceBar(
background: header(width,height,drawer)),
backgroundColor: Colors.white,
automaticallyImplyLeading: false,
elevation: 0,
),
];
},
controller: drawer.fromController.value,
body: tabBarView(width, height,drawer)));
}

header(width,height,drawer) {
return Container(
height: 450,
width: width,
color: Colors.purple,
);
}

tabBar(width,drawer) {
return Container(
width: width,
color: Colors.amber,
child: Stack(
alignment: Alignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
animateTo(0,drawer);
},
child: Container(
alignment: Alignment.center,
height: 50,
child: Obx(()=>Text('Page 1',
style: TextStyle(color: drawer.page.value == 0 ? Colors.white : Colors.grey))),
width: width * .23),
),
InkWell(
onTap: () {
animateTo(1,drawer);
},
child: Container(
alignment: Alignment.center,
height: 50,
child: Obx(()=>Text('Page2',
style: TextStyle(color: drawer.page.value == 1 ? Colors.white : Colors.grey))),
width: width * .23),
),
InkWell(
onTap: () {
animateTo(2,drawer);
},
child: Container(
alignment: Alignment.center,
height: 50,
child: Obx(()=>Text('Page 3',
style: TextStyle(color: drawer.page.value == 2 ? Colors.white : Colors.grey))),
width: width * .23),
),
InkWell(
onTap: () {
animateTo(3,drawer);
},
child: Container(
alignment: Alignment.center,
height: 50,
child: Obx(()=>Text('Page 4',
style: TextStyle(color: drawer.page.value == 3 ? Colors.white : Colors.grey))),
width: width * .23),
),
],
),
],
),
);
}

tabBarView(width, height,drawer) {
return Container(
height: height - 140,
padding: EdgeInsets.only(bottom: 65),
child: PageView(
children: <Widget>[
Container(
height: height,
alignment: Alignment.center,
child: Text('Page 1'),
),
Container(
height: height,
alignment: Alignment.center,
child: Text('Page 2'),
),
Container(
height: height,
alignment: Alignment.center,
child: Text('Page 3'),
),
Container(
height: height,
alignment: Alignment.center,
child: Text('Page 4'),
),
],
controller: drawer.controller.value,
onPageChanged: (value){
drawer.onPageChanged(value);
},
pageSnapping: true,
),
);
}

animateTo(int input,drawer) {
drawer.onPageChanged(input);
drawer.animateTo(input);
}

}

so we do’nt use any StatefulWidget but there is no change on our last result!

Congratulations! this is one of the steps to be a developer who respect mvc architecture :)

full code is available on this link:

hi I am flutter developer and excited on learning new topics

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store