skip to Main Content

I am trying to make a Single Page Application. I have 4 stateful classes:

  1. AppBar – This widget is being used to create an appBar on the top of the website with headers like: "Home", "About", "Contact Us", etc.
  2. Body – This widget is used to house all the contents of the headers which are the in the AppBar (Home is a container, About is another container, Contact Us is another container).
  3. HomeScreen – This is where I am combining AppBar and Body. This is wrapped inside SingleChildScrollView and is scrollable.

When I click on Home in AppBar, I want to scroll down to the respective section in the Body, likewise for other headers in the AppBar.

I have onTap() defined in AppBar and _scrollToHome() defined in HomeScreen. I tried to use Scrollable.ensureVisible() to scroll down to the respective section, but I am super confused as to how to link these two widgets.

Is this even the right approach or should I do something else to achieve the above functionality.

Below is the code:

  1. Home_Screen:
class HomeScreen extends StatefulWidget {
 const HomeScreen({Key key,}) : super(key: key);

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

class _HomeScreenState extends State<HomeScreen> {
 GlobalKey homeKey;
 final ScrollController _scrollController = ScrollController();
 double _scrollPosition = 0;
 double _opacity = 0;

 _scrollListener() {
   setState(() {
     _scrollPosition = _scrollController.position.pixels;
   });
 }

 @override
 void initState() {
   _scrollController.addListener(_scrollListener);
   super.initState();
 }

 void _scrollToHome() {
   Scrollable.ensureVisible(
     homeKey.currentContext,
     duration: Duration(seconds: 2),
     curve: Curves.fastOutSlowIn,
   );
 }

 @override
 Widget build(BuildContext context) {
   var screenSize = MediaQuery.of(context).size;
   _opacity = _scrollPosition < screenSize.height * 0.40
       ? _scrollPosition / (screenSize.height * 0.40)
       : 1;

   return Scaffold(
     appBar: PreferredSize(
             preferredSize: Size(screenSize.width, 100),
             child: CustomAppBar(opacity: _opacity),
           ),
     body: SingleChildScrollView(
             controller: _scrollController,
             child: Column(
               children: [
                 Stack(
                   children: [
                     Container(
                       // backgroundImage - Can be ignored
                     ),
                     Column(
                       children: [
                         Container(
                           // Home - Container
                         ),
                         Container(
                           //About - container
                         ),
                         Container(
                           //Contact Us - container
                         ),
                       ],
                     ),
                   ],
                 ),
               ],
             ),
           ),
   );
 }
}

Below is the code for AppBar:

class CustomAppBar extends StatefulWidget {
  final double opacity;

  CustomAppBar({
    Key key,
    this.opacity,
  }) : super(key: key);

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

class _CustomAppBarState extends State<CustomAppBar> {
  @override
  Widget build(BuildContext context) {
    var screenSize = MediaQuery.of(context).size;

    return PreferredSize(
      preferredSize: Size(screenSize.width, 70),
      child: Container(
        margin: EdgeInsets.all(10),
        color: Colors.black.withOpacity(widget.opacity),
        child: Padding(
          padding: EdgeInsets.all(10),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Expanded(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: <Widget>[
                    InkWell(
                      onTap: () {
                        //HomeScreen._scrollToHome(); - this is where I am confused
                      },
                      // Image which on click takes me to Home
                      ),
                    ),
                    Spacer(),
                    MenuItem(
                      title: "About",
                      press: () => {
                        //Navigate to About
                      },
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

2

Answers


  1. Chosen as BEST ANSWER

    I was doing it wrong all the time. All I had to do was create global keys for all the sections in the HomeScreen. Then pass these keys to the AppBar and implement _scrollToHome/_scrollToWidget function in the AppBar.

    In the HomeScreen I had to set the keys to the respective sections where they are getting created in the build().


  2. You could call a public method in another widget. In your case, you have to:

    1. Make your void _scrollToHome() to be public: void scrollToHome().

    2. Make its widget class to be public: class _HomeScreenState become class HomeScreenState.

    3. Declare a global variable and set it to be a Global key of above class:

      final homescreenKey = GlobalKey<HomeScreenState>();

    4. Set this key when you create the HomeScreen(homescreenKey).

    5. Call public method via this key:

      homescreenKey.currentState!.scrollToHome();

    Hope this helps.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search