Flutter TextFormField异常:平板模拟器中键盘弹出后立即消失
Flutter登录界面部分平板模拟器键盘弹出后立即消失问题
问题描述
开发登录界面时,使用TextFormField实现邮箱和密码输入功能,在部分平板模拟器中点击输入框后键盘会立即消失,但真机及部分模拟器运行正常,另外三款模拟器存在此问题。
解决方案
1. 用Form组件包裹输入框
代码中定义了_formKey但未使用Form组件管理输入框,TextFormField在Form容器内可更稳定地维护状态与焦点,避免因状态异常丢失焦点:
// 在build方法中,将emailField和passwordField包裹在Form内 Form( key: _formKey, child: Column( children: [ emailField, SizedBox(height: screenHeight * 0.02), passwordField, ], ), )
2. 移除控制器的static修饰符
静态控制器易引发跨实例状态冲突,改为在State类内初始化并在销毁时释放:
// 替换原静态控制器 final _emailController = TextEditingController(); final _passwordController = TextEditingController(); // 在dispose方法中释放资源 @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); }
3. 修复屏幕适配函数问题
代码中调用的sizeHintText()未提供实现,若该函数的计算逻辑在键盘弹出时触发布局重建,会导致输入框丢失焦点。建议确保该函数返回值稳定,或直接使用基于MediaQuery的可靠计算方式。
4. 调整Scaffold的resizeToAvoidBottomInset属性
部分场景下该属性的自动调整会引发布局波动,尝试设置为false测试:
Scaffold( resizeToAvoidBottomInset: false, // 其他代码 )
5. 手动管理焦点
为输入框添加FocusNode,手动控制焦点请求,避免焦点被意外移除:
// 在State类中定义FocusNode final _emailFocusNode = FocusNode(); final _passwordFocusNode = FocusNode(); // 在dispose中释放 @override void dispose() { _emailFocusNode.dispose(); _passwordFocusNode.dispose(); super.dispose(); } // 给TextFormField绑定focusNode final emailField = TextFormField( focusNode: _emailFocusNode, // 其他属性 ); final passwordField = TextFormField( focusNode: _passwordFocusNode, // 其他属性 );
6. 检查模拟器配置
- 关闭模拟器的硬件键盘选项(模拟器设置 -> 键盘 -> 取消"Hardware keyboard present");
- 更新模拟器系统镜像或创建新的模拟器实例测试。
相关代码
导入代码
import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:prcarpolimi/Internet/NetworkCheck.dart'; import 'package:prcarpolimi/auth/signUp.dart'; import 'package:prcarpolimi/auth/forgot_password.dart'; import 'package:prcarpolimi/models/marker_to_pass.dart'; import 'package:prcarpolimi/models/userModel.dart'; import 'package:prcarpolimi/homepage.dart'; import 'package:prcarpolimi/services/services.dart'; import '../models/static_user.dart'; import 'package:intl/intl.dart';
实现代码
final Service loginService; const Login({Key? key, required this.loginService}) : super(key: key); @override State<Login> createState() => _LoginState(); } class _LoginState extends State<Login> { UserModel userModel = UserModel(); static final _emailController = TextEditingController(); static final _passwordController = TextEditingController(); static final _formKey = GlobalKey<FormState>(); bool from = true; Future<User?> loginUsingEmailPassword( {required String email, required String password, required BuildContext context}) async { User? user; try { user = await widget.loginService.signInWithemailandpass(email, password); } on FirebaseAuthException catch (e) { if (e.code == "user-not-found") {} Fluttertoast.showToast( msg: 'Login failed :( wrong email or password', fontSize: 20); return null; } showDialog( context: context, barrierDismissible: false, builder: (context) => const Center(child: CircularProgressIndicator()), ); return user; } _finishReservation(User user) async { var data = await widget.loginService .firebasefirestore() .collection('users') .doc(user.uid) .collection('booking-out') .get(); if (data.docs.isNotEmpty) { for (var bookOut in data.docs) { String data = bookOut.data()['date']; final splitted = data.split('-'); String finalDate = splitted[1]; DateTime dayEnd = DateFormat("dd/MM/yyyy").parse(finalDate); if (dayEnd.compareTo(DateTime.now()) < 0) { await widget.loginService .firebasefirestore() .collection('users') .doc(user.uid) .collection('booking-out') .doc(bookOut.data()['bookingId']) .update({'status': 'f'}); } } } var data2 = await widget.loginService .firebasefirestore() .collection('users') .doc(user.uid) .collection('cars') .get(); if (data2.docs.isNotEmpty) { for (var car in data.docs) { await widget.loginService .firebasefirestore() .collection('users') .doc(car.data()['uid']) .collection('cars') .doc(car.data()['cid']) .collection('booking-in') .get() .then((ds) async { if (ds.docs.isNotEmpty) { for (var book in ds.docs) { String data = book.data()['date']; final splitted = data.split('-'); String finalDate = splitted[1]; DateTime dayEnd = DateFormat("dd/MM/yyyy").parse(finalDate); if (dayEnd.compareTo(DateTime.now()) < 0) { await widget.loginService .firebasefirestore() .collection('users') .doc(user.uid) .collection('cars') .doc(book.data()['cid']) .collection('booking-in') .doc(book.data()['bookingId']) .update({'status': 'f'}); } } } }); } } } @override Widget build(BuildContext context) { final screenHeight = MediaQuery.of(context).size.height; final screenWidth = MediaQuery.of(context).size.width; final screenText = MediaQuery.of(context).textScaleFactor; final emailField = TextFormField( key: const ValueKey(1), autofocus: false, style: TextStyle(fontSize: sizeHintText()), controller: _emailController, keyboardType: TextInputType.emailAddress, onSaved: (value) { _emailController.text = value!; }, textInputAction: TextInputAction.next, decoration: InputDecoration( prefixIcon: Icon(Icons.mail, size: sizeHintText()), contentPadding: EdgeInsets.fromLTRB(screenWidth * 0.02, screenHeight * 0.015, screenWidth * 0.02, screenHeight * 0.015), hintText: "Email", hintStyle: TextStyle(fontSize: sizeHintText()), border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)))); final passwordField = TextFormField( key: const ValueKey(2), autofocus: false, style: TextStyle(fontSize: sizeHintText()), controller: _passwordController, obscureText: true, onSaved: (value) { _passwordController.text = value!; }, textInputAction: TextInputAction.done, decoration: InputDecoration( prefixIcon: Icon(Icons.vpn_key, size: sizeHintText()), contentPadding: EdgeInsets.fromLTRB(screenWidth * 0.02, screenHeight * 0.015, screenWidth * 0.02, screenHeight * 0.015), hintText: "Password", hintStyle: TextStyle(fontSize: sizeHintText()), border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)))); return Scaffold( resizeToAvoidBottomInset: true, backgroundColor: Colors.white, body: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: screenHeight * 0.1), Text("Welcome to PrCar!", textAlign: TextAlign.center, style: TextStyle( color: Colors.black, fontSize: screenText * 45, fontWeight: FontWeight.bold)), SizedBox(height: screenHeight * 0.05), SizedBox( height: screenHeight * 0.2, child: Image.asset("assets/prcarlogo.png", fit: BoxFit.contain)), SizedBox(height: screenHeight * 0.05), emailField, SizedBox(height: screenHeight * 0.02), passwordField, SizedBox(height: screenHeight * 0.05), Row(children: [ SizedBox(width: screenWidth * 0.06), GestureDetector( child: Text('Forgot password?', style: TextStyle( decoration: TextDecoration.underline, color: Colors.redAccent, fontSize: screenText * 16)), onTap: () => Navigator.of(context).push(MaterialPageRoute( builder: (context) => const ForgotPasswordPage(), ))), Text(' or ', style: TextStyle(fontSize: screenText * 12)), GestureDetector( key: Key("New Account"), child: Text("Don't have an account?", textAlign: TextAlign.start, style: TextStyle( decoration: TextDecoration.underline, color: Colors.redAccent, fontSize: screenText * 16)), onTap: () => Navigator.push( context, MaterialPageRoute( builder: (context) => const SignUp()))) ]), SizedBox(height: screenHeight * 0.06), Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Container( key: const Key("clickButtom"), height: screenHeight * 0.07, width: screenWidth * 0.85, child: MaterialButton( color: Colors.redAccent, onPressed: () async { if (await NetworkCheck().check()) { User? user = await loginUsingEmailPassword( email: _emailController.text, password: _passwordController.text, context: context); if (user != null) { await widget.loginService .firebasefirestore() .collection('users') .doc(user.uid) .get() .then((ds) { userModel = UserModel.fromMap(ds); StaticUser.email = userModel.email!; StaticUser.uid = userModel.uid!; StaticUser.firstName = userModel.firstName!; StaticUser.secondName = userModel.secondName!; PassMarker.from = true; _finishReservation(user); Navigator.push( context, MaterialPageRoute( builder: (context) => HomePage( homePageService: Service()))); }); } } else { Fluttertoast.showToast( msg: 'No internet connection', fontSize: 20); } }, child: Text("Login", style: TextStyle( color: Colors.white, fontSize: screenText * 25))), decoration: BoxDecoration( color: Colors.deepPurple, borderRadius: BorderRadius.circular(12), boxShadow: const [ BoxShadow( color: Colors.deepPurple, spreadRadius: 6, blurRadius: 3) ])) ]) ]))); } }
内容的提问来源于stack exchange,提问作者Francesco Marin Vargas
相关产品推荐
相关产品推荐

