Flutter中ScreenUtilInit致TextFormField获焦后页面闪退求助
登录页输入框获焦后页面自动关闭(仅ScreenUtilInit初始化时触发)
登录页包含用户名、密码两个TextFormField,当任一输入框获得焦点、键盘弹出后,页面会立即关闭。经排查,仅在MaterialApp外层使用screenutil包的ScreenUtilInit初始化时才会出现此问题,移除该初始化后功能正常。
项目采用Clean Architecture+MVVM架构,曾尝试将TextFormField从model文件移至状态类中,也调整过ScreenUtilInit的designSize,但均未解决问题。
相关代码
app_start.dart
ScreenUtilInit( designSize: const Size(375, 667), builder: (_, child) { return MaterialApp() });
login_page.dart
Form( key: model.loginFormKey, child: Column( children: [ PrimaryTextField( controller: model.emailAddressController, label: Strings.of(context).email_address, keyBoardType: TextInputType.emailAddress, validator: Validator.emailValidator, ), PrimaryTextField( controller: model.passwordController, label: Strings.of(context).password, validator: Validator.pwdValidator, ), ] ) )
Primary_text_field(状态类实现)
class _PrimaryTextFieldState extends State<PrimaryTextField> { ValueNotifier<bool> validateForm = ValueNotifier<bool>(false); @override Widget build(final BuildContext context) => ValueListenableBuilder<bool>( valueListenable: validateForm, builder: ( context, listenValue, child, ) { return TextFormField( focusNode: widget.focusNode, autocorrect: false, maxLines: widget.maxLines ?? 1, controller: widget.controller, onChanged: (value) { validateForm.value = widget.validator!(widget.controller.text) != null; }, inputFormatters: widget.inputFormatters, validator: widget.validator, autovalidateMode: AutovalidateMode.onUserInteraction, keyboardType: widget.keyBoardType, obscureText: widget.isPassword, decoration: InputDecoration( labelText: widget.label, floatingLabelStyle: listenValue ? AppTextStyle().notificationsErrorBodyXS : AppTextStyle().bodyNeutralTwoLeftLowEmphasis500, labelStyle: AppTextStyle().bodyNeutralTwoLeftLowEmphasis500, errorStyle: AppTextStyle().notificationsErrorBodyXS, suffixIconConstraints: const BoxConstraints( minHeight: 14, minWidth: 20, ), suffixIcon: widget.isEyeSymbol ? InkWell( onTap: widget.onSuffixTap, child: widget.isPassword ? Padding( padding: const EdgeInsets.only( right: 10, ), child: AppSvg.asset( AssetIcons.visibilityNotN1, ), ) : Padding( padding: const EdgeInsets.only( right: 10, ), child: AppSvg.asset( AssetIcons.visibilityN1, ), )) : const SizedBox(), errorMaxLines: 2, fillColor: widget.bgColor ?? AppColorStyle().neutral_500_10, filled: true, contentPadding: EdgeInsets.symmetric( horizontal: 16.w, vertical: widget.verticalPadding ?? 16.h, ), focusedBorder: UnderlineInputBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular( widget.borderRadius ?? 4.r, ), topRight: Radius.circular( widget.borderRadius ?? 4.r, ), ), borderSide: BorderSide( color: AppColorStyle().neutral_700, width: 2.w, ), ), enabledBorder: UnderlineInputBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular( widget.borderRadius ?? 4.r, ), topRight: Radius.circular( widget.borderRadius ?? 4.r, ), ), borderSide: BorderSide( color: AppColorStyle().neutral_500, ), ), ), ); }, ); }
日志信息
onRequestShow at ORIGIN_CLIENT_SHOW_SOFT_INPUT reason SHOW_SOFT_INPUT D/InputMethodManager(13938): showSoftInput() view=io.flutter.embedding.android.FlutterView{fe12c43 VFE...... .F....ID 0,0-1080,2274 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT D/EGL_emulation(13938): app_time_stats: avg=46.56ms min=2.02ms max=709.37ms count=22 D/InsetsController(13938): show(ime(), fromIme=true) I/ImeTracker(13938): com.example.dev:1d3363d3: onRequestHide at ORIGIN_CLIENT_HIDE_SOFT_INPUT reason HIDE_SOFT_INPUT W/WindowOnBackDispatcher(13938): sendCancelIfRunning: isInProgress=falsecallback=ImeCallback=ImeOnBackInvokedCallback@243525502 Callback=android.window.IOnBackInvokedCallback$Stub$Proxy@ce0499f I/ImeTracker(13938): db2c8a5c: onCancelled at PHASE_CLIENT_ANIMATION_CANCEL D/EGL_emulation(13938): app_time_stats: avg=4734.11ms min=1.42ms max=89123.80ms count=19 D/EGL_emulation(13938): app_time_stats: avg=66.93ms min=7.93ms max=334.65ms count=15 I/ImeTracker(13938): com.example.dev:87b7a37: onRequestHide at ORIGIN_CLIENT_HIDE_SOFT_INPUT reason HIDE_SOFT_INPUT_BY_INSETS_API I/ImeTracker(13938): com.example.dev:1d3363d3: onHidden
解决建议
- 调整ScreenUtilInit层级:将ScreenUtilInit移至MaterialApp内部,比如作为首页或登录页的父节点,而非包裹整个MaterialApp,避免全局尺寸计算导致的页面重建。
- 补充ScreenUtil初始化参数:添加
minTextAdapt: true,稳定文本适配逻辑,减少键盘弹出时的布局波动。 - 追踪焦点变化:给TextFormField绑定自定义FocusNode,在
onFocusChange中打印日志,确认是否是焦点丢失触发了页面路由的pop操作。 - 防止页面意外销毁:给登录页添加
AutomaticKeepAliveClientMixin,重写wantKeepAlive返回true,避免页面因布局重绘被销毁。 - 升级screenutil版本:如果使用旧版本,更新至最新稳定版,可能已修复键盘弹出引发的布局异常问题。
- 禁用自动尺寸重置:在ScreenUtilInit中设置
splitScreenMode: true,或手动指定适配模式,避免键盘弹出时触发全局尺寸重新计算。
内容的提问来源于stack exchange,提问作者Aravindhan Dbest
相关产品推荐
相关产品推荐

