Flutter Web输入框获焦时Hover返回按钮触发错误的解决咨询
我在开发一个小型Flutter Web应用,页面采用Stack布局,包含表单与返回上一页按钮。当输入框处于获焦状态,鼠标hover返回按钮时,会触发如下错误:
errors.dart:296 Uncaught Error
at Object.throw_ [as throw] (errors.dart:296:3)
at Object.assertFailed (errors.dart:29:3)
at Object._computeOffsetForInputs (event_position_helper.dart:64:37)
at Object.computeEventOffsetToTarget (event_position_helper.dart:32:14)
at [_convertEventsToPointerData] (pointer_binding.dart:1037:30)
at pointer_binding.dart:976:9
at pointer_binding.dart:930:7
at loggedHandler (pointer_binding.dart:531:9)
at Object._checkAndCall (operations.dart:426:37)
at Object.dcall (operations.dart:431:39)
at ret (js_allow_interop_patch.dart:17:11)
输入框未获焦时运行正常,求修复方案。
环境信息
- Flutter version 3.22.0
- Dart version 3.4.0
- DevTools version 2.34.3
核心代码
ResetPasswordView 代码
class ResetPasswordView extends StatelessWidget { const ResetPasswordView({ super.key, }); Widget formSection(BuildContext context, ResetPasswordViewModel viewModel) { final AppLocalizations l10n = AppLocalizations.of(context); return SingleChildScrollView( // TODO Max : A bouger dans le layout physics: const BouncingScrollPhysics(), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ AppSpacer.p64(), // TODO Replace by image if (MediaQuery.of(context).size.width > 650) const Placeholder(fallbackHeight: 100), AppSpacer.p8(), Form( key: viewModel.formKey, child: FocusScope( node: viewModel.focusScopeNode, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ InputEmail( controller: viewModel.emailController, autofocus: true, enabled: !viewModel.isBusy, onChange: viewModel.onFormChange, onEditingComplete: () async => viewModel.onEditingCompleted( context: context, submit: true), ), if (viewModel.busy(viewModel.isResetPasswordButtonEnabled)) const CircularProgressIndicator() else SizedBox( width: double.infinity, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 12.0, vertical: 8.0), child: FilledButton( onPressed: viewModel.isResetPasswordButtonEnabled ? () async => viewModel.resetPassword(context) : null, child: Text( l10n.reset_password_view_reset_password, style: const TextStyle(fontSize: 20.0), ), ), ), ), ], ), ), ), ], ), ); } @override Widget build(BuildContext context) { final AppLocalizations l10n = AppLocalizations.of(context); final GoRouter router = GoRouter.of(context); return ViewModelBuilder<ResetPasswordViewModel>.reactive( viewModelBuilder: () => ResetPasswordViewModel(router, l10n), builder: (context, viewModel, _) => AppScaffold( body: AuthenticationLayout( widget: formSection(context, viewModel), isBackArrow: true, ), ), ); } }
AuthenticationLayout 代码
class AuthenticationLayout extends StatelessWidget { final Widget widget; final bool isBackArrow; const AuthenticationLayout({ required this.widget, this.isBackArrow = false, super.key, }); Widget rightForm(Widget widget, double screenHeight, double width) { return new Container( width: width, height: screenHeight, decoration: new BoxDecoration(color: Color.fromRGBO(12, 71, 28, 1)), // TODO Max : A changer et à sortir dans le thème child: Padding( padding: EdgeInsets.symmetric(horizontal: 64), child: widget, ), ); } @override Widget build(BuildContext context) { final MediaQueryData m = MediaQuery.of(context); return Stack( children: [ rightForm(widget, m.size.height, m.size.width), if (isBackArrow) InkWell( onHover: (value) { print(value); }, onTap: () => GoRouter.of(context).pop(), child: Icon(Icons.arrow_back_rounded)) ], ); } }
这个问题是Flutter Web在处理焦点元素与Stack上层hover交互时的已知布局/事件计算bug,核心原因是InkWell的hover事件在计算偏移时,与下层获焦的输入框元素出现坐标冲突。以下是几种可行的修复方案:
方案1:给返回按钮添加定位与内边距
在AuthenticationLayout的InkWell外层包裹Positioned明确按钮位置,同时给Icon添加内边距,避免hover事件触发时的坐标计算异常:
if (isBackArrow) Positioned( top: 20, left: 20, child: InkWell( onHover: (value) { print(value); }, onTap: () => GoRouter.of(context).pop(), child: const Padding( padding: EdgeInsets.all(8.0), child: Icon(Icons.arrow_back_rounded), ), ), )
方案2:调整焦点控制逻辑
原代码中InputEmail设置了autofocus: true且嵌套在FocusScope下,易导致Web端焦点绑定异常。移除自动聚焦,改为在ViewModel初始化时手动请求焦点:
在ResetPasswordViewModel中添加初始化逻辑:
class ResetPasswordViewModel extends ReactiveViewModel { // ... 原有代码 final FocusScopeNode focusScopeNode = FocusScopeNode(); final TextEditingController emailController = TextEditingController(); @override void onInit() { super.onInit(); // 延迟请求焦点,避免布局未完成时触发异常 Future.delayed(const Duration(milliseconds: 100), () { focusScopeNode.requestFocus(); }); } // ... 原有代码 }
同时将InputEmail的autofocus: true改为autofocus: false。
方案3:替换InkWell为GestureDetector
InkWell在Web端的hover事件实现存在兼容性问题,改用GestureDetector的onEnter/onExit替代hover逻辑:
if (isBackArrow) Positioned( top: 20, left: 20, child: GestureDetector( onTap: () => GoRouter.of(context).pop(), onEnter: (_) => print(true), onExit: (_) => print(false), child: const Padding( padding: EdgeInsets.all(8.0), child: Icon(Icons.arrow_back_rounded), ), ), )
方案4:升级Flutter版本
该bug在Flutter 3.24+稳定版中已被官方修复,若环境允许,直接执行升级命令即可解决:
flutter upgrade
内容的提问来源于stack exchange,提问作者Redfire

