Flutter: push a new page and scroll down to certain widget

341 Views Asked by At

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.

enter image description here

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?

0

There are 0 best solutions below