Flutter登录页小屏设备键盘遮挡TextField且无法滚动问题求助
解决小屏设备键盘遮挡TextField且无法滚动的问题
你的代码里有两个核心问题导致了该现象,修改以下两处即可解决:
关键修改点
- 开启页面自动避让键盘:Scaffold的
resizeToAvoidBottomInset设为false会阻止页面调整高度避开键盘,改成true。 - 移除固定高度限制:外层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
相关产品推荐
相关产品推荐

