So, I have a login page with two TextFields, and then a RaisedButton for login at the very bottom. When I tap on the email field and the keyboard pops up, I would like for the SingleChildScrollView (the parent of everything on the page) to scroll to the maxScrollExtent.
Things I have tried that haven't worked:
What almost works:
By almost, here's what I mean (excuse the GIF discoloration):
As you can see, it doesn't work the first time it focuses so I have to tap the password field, then retap the email field for it to animate. I have tried adding a delay (even up to 500ms) so that the viewport has time to fully resize before doing this, but that didn't work either.
If you recognize this login theme, that's because I adapted it from here. The file is pretty lengthy, but here are the relevant bits:
@override
void initState() {
super.initState();
scrollController = ScrollController();
focusNode = FocusNode();
focusNode.addListener(() {
if (focusNode.hasFocus) {
scrollController.animateTo(scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 500), curve: Curves.ease);
}
});
_emailFieldController = TextEditingController();
_passFieldController = TextEditingController();
_emailFieldController.addListener(() {
_emailText = _emailFieldController.text;
});
_passFieldController.addListener(() {
_passText = _passFieldController.text;
});
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
controller: scrollController,
child: Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
color: Colors.white,
image: DecorationImage(
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.05), BlendMode.dstATop),
image: AssetImage('assets/images/mountains.jpg'),
fit: BoxFit.cover,
),
),
child: new Column(
children: <Widget>[
// this is where all other widgets in the file are
Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 40.0, right: 40.0, top: 10.0),
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.deepPurple,
width: 0.5,
style: BorderStyle.solid),
),
),
padding: const EdgeInsets.only(left: 0.0, right: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: TextField(
controller: _emailFieldController,
keyboardType: TextInputType.emailAddress,
focusNode: focusNode,
obscureText: false,
textAlign: TextAlign.left,
decoration: InputDecoration(
border: InputBorder.none,
hintText: '[email protected]',
hintStyle: TextStyle(color: Colors.grey),
),
),
),
],
),
),
Any guidance would be greatly appreciated. Thank you!
You can create a ScrollController and pass it to the controller parameter of your scrolling widget. Then you can use the animateTo method to animate to an offset.
Use addPostFrameCallback
to listen after the widget was built.
_onLayoutDone(_){
FocusScope.of(context).requestFocus(focusNode);
}
@override
void initState() {
//... your stuff
WidgetsBinding.instance.addPostFrameCallback(_onLayoutDone);
super.initState();
}
UPDATE
I see the error, the first time you use scrollController.position.maxScrollExtent
the value is 0, after you tap on password textField and you change the focus to email
, now the maxScrollExtent
is different because the keyboard is open.
If you want to make it work, do a logic to calculate the space and set the value directly.
If you use
scrollController.animateTo(180.0,
duration: Duration(milliseconds: 500), curve: Curves.ease);
It should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With