I made a website with PageController to control the scroll of some screens. And I made a menu section with screen names, and when any of them are pressed, the user will navigate to the respective screen.
The app is working fine. But I refactored the menu button so I can control it's style, and to add animation in the future.
But when I refactored the button, I can't pass the PageController index, or the nextPage function.
That's why I thought of using the provider package. However, I didn't the package before, and my setup seems complex. For I should pass the PageController state to the button, and the button should send the nextPage function with a new number.
How can I achieve this?
Here's my code:
The button:
class NavigationButton extends StatefulWidget {
const NavigationButton({
Key key,
this.title,
// this.onPressed
}) : super(key: key);
final String title;
// final VoidCallback onPressed;
@override
_NavigationButtonState createState() => _NavigationButtonState();
}
class _NavigationButtonState extends State<NavigationButton> {
bool isHovering = false;
@override
Widget build(BuildContext context) {
return InkWell(
child: Container(
decoration: BoxDecoration(
border: isHovering == true
? Border(right: BorderSide(color: Colors.blueGrey, width: 10))
: Border(right: BorderSide.none)
),
child: Text(
widget.title,
style: TextStyle(
color: Colors.white,
fontSize: 25
)
)
),
onHover: (value) {
setState(() {
isHovering = value;
});
},
onTap: () {}
);
}
}
The main layout:
class MainLayout extends StatefulWidget {
@override
_MainLayoutState createState() => _MainLayoutState();
}
class _MainLayoutState extends State<MainLayout> {
int _index = 0;
int selectedButton;
bool isHovering = false;
PageController pageController = PageController();
final List<String> _sectionsName = [
"Home",
"About",
"Services",
"Portfolio",
"Testimonials",
"News",
"Contact"
];
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: MediaQuery.of(context).size.width > 760
? null
: AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent
),
drawer: MediaQuery.of(context).size.width < 760 ? DrawerWidget() : null,
body: Stack(
children: [
// MediaQuery.of(context).size.width < 760 ? Container() : DrawerWidget(),
Listener(
onPointerSignal: (pointerSignal) {
if (pointerSignal is PointerScrollEvent) {
if (pointerSignal.scrollDelta.dy > 0) {
if(_index < _sectionsName.length) {
// int newIndex = _index + 1;
// pageController.jumpToPage(newIndex);
pageController.nextPage(
curve: Curves.easeIn, duration: Duration(milliseconds: 500)
);
}
}
else
{
if(_index < _sectionsName.length) {
// int newIndex = _index - 1;
// pageController.jumpToPage(newIndex);
pageController.previousPage(
curve: Curves.easeIn, duration: Duration(milliseconds: 500)
);
}
}
}
},
child: PageView(
controller: pageController,
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
children: [
HeroSection(),
AboutSection(),
ServicesSection(),
PortfolioSection(),
TestimonialsSection(),
NewsSection(),
ContactSection()
],
onPageChanged: (index) {
_index = index;
}
)
),
MediaQuery.of(context).size.width < 760
? Container()
: Align(
alignment: Alignment.centerRight,
child: Container(
height: 205,
width: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int index = 0; index < _sectionsName.length; index++)
NavigationButton(title: _sectionsName[index])
]
)
)
)
]
)
);
}
}
Thanks in advance...
Edit:
I followed @EdwynZN answer, and it was very helpful. But I had to pass the index from MainLayout to NavigationButton like so:
MainLayout: NavigationButton(title: _sectionsName[index], index: index)
NavigationButton: Added this to the constructor: this.index. And this to the class: final int index;
finally:
onTap: () {
controller.animateToPage(widget.index, curve: Curves.easeIn, duration: Duration(milliseconds: 500));
}
I hope this will help someone some day...
Using Provider you could wrap the widget that will need PageController with a ChangeNotifierProvider.value
ChangeNotifierProvider.value(
value: pageController,
Align(
alignment: Alignment.centerRight,
child: Container(
height: 205,
width: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int index = 0; index < _sectionsName.length; index++)
NavigationButton(title: _sectionsName[index])
]
)
)
),
),
Then all NavigationButton can access pageController as an inheretedWidget
class _NavigationButtonState extends State<NavigationButton> {
bool isHovering = false;
PageController controller;
int index;
@override
void didChangeDependencies() {
controller = Provider.of<PageController>(context); //you can read the pagecontroller here
index = controller.page.round(); // will update when the page changes so you can do some logic with colors
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return InkWell(
child: Container(
decoration: BoxDecoration(
border: isHovering == true
? Border(right: BorderSide(color: Colors.blueGrey, width: 10))
: Border(right: BorderSide.none)
),
child: Text(
widget.title,
style: TextStyle(
color: Colors.white,
fontSize: 25
)
)
),
onHover: (value) {
setState(() {
isHovering = value;
});
},
onTap: () {}
);
}
}