当TextField获得焦点时,如何滚动到SingleChildScrollView的底部?

时间:2019-04-15 22:36:33

标签: android ios dart flutter flutter-animation

因此,我有一个带有两个TextField的登录页面,然后在最底部有一个RaisedButton进行登录。当我点击电子邮件字段并弹出键盘时,我希望SingleChildScrollView(页面上所有内容的父级)滚动到maxScrollExtent。

我尝试过的无效的东西:

  • 利用Scaffold的自动执行功能(Scaffold是应用程序中所有内容的父窗口小部件)
  • 使用this tutorial在其中创建帮助窗口小部件。也使用WidgetBindingsObserver,但整个教程对我而言不起作用。我不知道WidgetBindingsObserver是否仍然会有所帮助。

几乎可行的方法:

  • 将FocusNode附加到TextForm,然后在initState()中附加一个侦听器,当其具有焦点时,该侦听器将动画化为maxScrollExtent。

差不多,这就是我的意思(对不起,GIF变色):

enter image description here

如您所见,它在第一次聚焦时不起作用,因此我必须点击密码字段,然后重新输入电子邮件字段以使其动画。我尝试添加一个延迟(甚至最多500ms),以使视口在执行此操作之前有时间完全调整大小,但这也不起作用。

如果您识别出此登录主题,那是因为我改编自here。该文件很长,但是这里是相关的位:

@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: 'coolname@bestemail.com',
                        hintStyle: TextStyle(color: Colors.grey),
                      ),
                    ),
                  ),
                ],
              ),
            ),

任何指导将不胜感激。谢谢!

1 个答案:

答案 0 :(得分:2)

在构建窗口小部件后,使用addPostFrameCallback进行监听。

          _onLayoutDone(_){
              FocusScope.of(context).requestFocus(focusNode);
          } 

          @override
          void initState() {
            //... your stuff

            WidgetsBinding.instance.addPostFrameCallback(_onLayoutDone);
            super.initState();
          }

更新

我看到了错误,第一次使用scrollController.position.maxScrollExtent的值为0,是在您点击密码textField并将焦点更改为email之后,现在maxScrollExtent有所不同因为键盘是打开的。

如果要使其起作用,请执行逻辑运算以计算空间并直接设置值。

如果您使用

 scrollController.animateTo(180.0,
        duration: Duration(milliseconds: 500), curve: Curves.ease);

应该可以。