I have a web app in Flutter. My app bar has 5 buttons. Four of them scroll within the first page. The 5th one pushes a new page.
There are three different widgets on stage:
- page A (HomeView)
- page B (ContactView)
- the AppBar
When the user is on page A, the buttons of page A should scroll down or up, and the button of page B should push the new view. When the user is on page B (contact) and presses any of the buttons of page A, it should push the home page and scroll to the required position.
I tried to do this with ScrollController.animateTo
and Riverpod for state management, but I couldn't do it. The page scrolled down but, for some reason, it scrolled up again and didn't maintain position. I couldn't find any related post on the Internet and keepScrollOffset
was not the answer. I also didn't know how to scroll to the desired position when the user is not on 0 (it scrolled down from current position).
After that, I tried to do it with GlobalKeys. It worked, up to a certain point. On page A, everything worked fine. However, from page B to page A, it said the keys are duplicated. I used pop
instead of pushNamed
as suggested in one of the answers of this post (the only answer that worked for me). The behavior was finally as expected, but I can't use pop
since I have more pages on the website, so I can't ensure that the user is coming from page A. I tried with pushReplacement
as the same answer gives that for an alternative option. It doesn't work now. When trying to go from page B (contact) to page A, it throws error Unexpected null value.
. Furthermore, I need to keep the back button functionality since it is a website. I would prefer, then, to not pop the current page from the stack.
The page A (HomeView) is built like this inside of a StatefulWidget:
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
controller: _scrollController,
child: Column(
children: [
HomeHeader(key: HomeView.homeKey),
WhatWeOffer(key: HomeView.servicesKey),
WhatWeDo(key: HomeView.referencesKey),
WhoWeAre(key: HomeView.aboutKey),
const LetsGetInTouch(),
],
),
);
}
The GlobalKeys are defined like this inside of the Page A (HomeView) widget:
static GlobalKey homeKey = GlobalKey(debugLabel: 'homeHeaderKey');
static GlobalKey servicesKey = GlobalKey(debugLabel: 'whatWeOfferKey');
static GlobalKey referencesKey = GlobalKey(debugLabel: 'whatWeDoKey');
static GlobalKey aboutKey = GlobalKey(debugLabel: 'whoWeAreKey');
This is how I implement the navigation to page A:
if (ModalRoute.of(context)?.settings.name != '/') {
Navigator.pushReplacementNamed(
context,
Navigation(context).routes["/"]!,
);
}
Scrollable.ensureVisible(
HomeView.homeKey.currentContext!,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
And this is how I implement the navigation to page B:
Navigator.pushReplacementNamed(
context,
Navigation(context).routes["/contact"]!,
),
Maybe GlobalKey is not the right approach to my problem? Is there any easier way to push a new page and scroll down to a widget?
EDIT:
I think the Unexpected null value
problem was caused because the view was not built yet when I was trying to call it. I solved it by making the functions async and waiting before using the keys:
TextButton(
child: Text('home', style: style),
onPressed: () async {
if (ModalRoute.of(context)?.settings.name != '/') {
Navigator.pushReplacementNamed(
context,
Navigation(context).routes['/']!,
);
await Future.delayed(const Duration(milliseconds: 500));
}
Scrollable.ensureVisible(
HomeView.homeKey.currentContext!,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
),
However, this does not solve my problem of the back button. As I use pushReplacementNamed
instead of pushNamed
, I can't go back. Is there a more optimal way to do this, where I can keep the back button functionality?