You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter登录页小屏设备键盘遮挡TextField且无法滚动问题求助

解决小屏设备键盘遮挡TextField且无法滚动的问题

你的代码里有两个核心问题导致了该现象,修改以下两处即可解决:

关键修改点

  1. 开启页面自动避让键盘:Scaffold的resizeToAvoidBottomInset设为false会阻止页面调整高度避开键盘,改成true。
  2. 移除固定高度限制:外层Container设置了height: double.infinity,导致SingleChildScrollView无法识别可滚动的剩余空间,删除这个属性。

修改后的完整代码

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  String? errorMessage = '';
  bool isLogin = true;

  final TextEditingController _controllerEmail = TextEditingController();
  final TextEditingController _controllerPassword = TextEditingController();

  Future<void> signInWithEmailAndPassword() async {
    try {
      await Auth().signInWithEmailAndPassword(
          email: _controllerEmail.text, password: _controllerPassword.text);
    } on FirebaseAuthException catch (e) {
      setState(() {
        errorMessage = e.message;
      });
    }
  }

  Future<void> createUserWithEmailAndPassword() async {
    try {
      await Auth().createUserWithEmailAndPassword(
          email: _controllerEmail.text, password: _controllerPassword.text);
    } on FirebaseAuthException catch (e) {
      setState(() {
        errorMessage = e.message;
      });
    }
  }

  Widget _title() {
    return const Text("MysteryMap");
  }

  Widget _entryField(String title, TextEditingController controller) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: TextField(
        obscureText: title == 'contraseña' ? true : false,
        controller: controller,
        decoration: InputDecoration.collapsed(
          hintText: title,
        ),
      ),
    );
  }

  Widget _errorMessage() {
    return Text(
      errorMessage! == '' ? '' : '$errorMessage !!!',
      style: TextStyle(fontSize: 10, color: Colors.white),
    );
  }

  Widget _submitButton() {
    return ElevatedButton(
      onPressed: () => isLogin
          ? signInWithEmailAndPassword()
          : createUserWithEmailAndPassword(),
      child: Text(
        isLogin ? 'Iniciar sesión' : 'Crear cuenta',
        style: TextStyle(color: Colors.white),
      ),
    );
  }

  Widget _loginOrRegisterButton() {
    return TextButton(
        onPressed: () {
          setState(() {
            isLogin = !isLogin;
          });
        },
        child: Text(
          isLogin ? 'Crear cuenta' : 'Ya tengo cuenta',
          style: TextStyle(color: Colors.white),
        ));
  }

  @override
  Widget build(BuildContext context) {
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ]);
    return SafeArea(
      child: Scaffold(
        // 修改点1:开启自动避让键盘
        resizeToAvoidBottomInset: true,
        body: Container(
          width: double.infinity,
          // 修改点2:移除固定高度,让内容决定容器高度
          decoration: BoxDecoration(
            image: DecorationImage(
              image: AssetImage("assets/fondo_login.jpeg"),
              fit: BoxFit.cover,
            ),
          ),
          padding: EdgeInsets.all(20),
          child: SingleChildScrollView(
            // 可选:添加底部padding,避免键盘弹出时内容贴边
            padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Row(
                  children: [
                    Container(
                      width: 80,
                      height: 80,
                      decoration: BoxDecoration(
                          image: DecorationImage(
                              image: AssetImage("assets/app_icon.png"))),
                    ),
                    Spacer(),
                  ],
                ),
                SizedBox(
                  height: 100,
                ),
                Align(
                    alignment: Alignment.topLeft,
                    child: Text(
                      "Email",
                      style: TextStyle(color: Colors.grey, fontSize: 18),
                    )),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                      decoration: BoxDecoration(
                        color: Colors.white,
                        borderRadius: const BorderRadius.all(
                          Radius.circular(100.0),
                        ),
                      ),
                      child: _entryField('email', _controllerEmail)),
                ),
                SizedBox(
                  height: 10,
                ),
                Align(
                    alignment: Alignment.topLeft,
                    child: Text(
                      "Contraseña",
                      style: TextStyle(color: Colors.grey, fontSize: 18),
                    )),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Container(
                      decoration: BoxDecoration(
                        color: Colors.white,
                        borderRadius: const BorderRadius.all(
                          Radius.circular(100.0),
                        ),
                      ),
                      child: _entryField('contraseña', _controllerPassword)),
                ),
                SizedBox(
                  height: 10,
                ),
                _errorMessage(),
                SizedBox(
                  height: 10,
                ),
                _submitButton(),
                SizedBox(
                  height: 10,
                ),
                _loginOrRegisterButton(),
                GestureDetector(
                    onTap: () {
                      Navigator.of(context).push(
                          MaterialPageRoute(builder: (context) => Plantilla()));
                    },
                    child: Text("fondo"))
              ],
            ),
          ),
        ),
      ),
    );
  }
}

额外优化(可选)

如果希望键盘弹出时自动滚动到聚焦的TextField,可以给密码输入框添加焦点监听:

// 在_state类中定义焦点节点
final FocusNode _passwordFocusNode = FocusNode();

// 在initState中添加监听
@override
void initState() {
  super.initState();
  _passwordFocusNode.addListener(() {
    if (_passwordFocusNode.hasFocus) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        Scrollable.ensureVisible(_passwordFocusNode.context!, duration: Duration(milliseconds: 300));
      });
    }
  });
}

// 修改_entryField方法接收focusNode参数
Widget _entryField(String title, TextEditingController controller, {FocusNode? focusNode}) {
  return Padding(
    padding: const EdgeInsets.all(8.0),
    child: TextField(
      focusNode: focusNode,
      obscureText: title == 'contraseña' ? true : false,
      controller: controller,
      decoration: InputDecoration.collapsed(
        hintText: title,
      ),
    ),
  );
}

// 在密码输入框处传入焦点节点
_entryField('contraseña', _controllerPassword, focusNode: _passwordFocusNode)

// 在dispose中释放资源
@override
void dispose() {
  _passwordFocusNode.dispose();
  _controllerEmail.dispose();
  _controllerPassword.dispose();
  super.dispose();
}

内容的提问来源于stack exchange,提问作者mvasco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 23:50:00