I am trying to make a Single Page Application. I have 4 stateful classes:
- AppBar – This widget is being used to create an appBar on the top of the website with headers like: "Home", "About", "Contact Us", etc.
- 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).
- 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:
- 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
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 theAppBar
and implement_scrollToHome
/_scrollToWidget
function in theAppBar
.In the
HomeScreen
I had to set the keys to the respective sections where they are getting created in thebuild()
.You could call a public method in another widget. In your case, you have to:
Make your void _scrollToHome() to be public: void scrollToHome().
Make its widget class to be public: class _HomeScreenState become class HomeScreenState.
Declare a global variable and set it to be a Global key of above class:
final homescreenKey = GlobalKey<HomeScreenState>();
Set this key when you create the HomeScreen(homescreenKey).
Call public method via this key:
homescreenKey.currentState!.scrollToHome();
Hope this helps.