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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 03:02:05