iOS 18下Flutter InAppWebView上下文菜单触发UICommand崩溃问题及相关技术咨询
iOS 18下Flutter InAppWebView上下文菜单触发UICommand崩溃问题及相关技术咨询
问题背景
我在iOS 18设备上遇到一个崩溃问题:当点击flutter_inappwebview内部的原生上下文菜单项时,App会崩溃,崩溃日志指向UIKitCore中的UICommand performWithSender:target:方法。以下是详细的崩溃信息、相关代码和核心疑问。
崩溃日志(摘录)
Crashed: com.apple.main-thread 0 ??? 0x103bf400c (Missing) 1 UIKitCore 0x136b698 -[UICommand performWithSender:target:] + 172 2 UIKitCore 0x1550e64 -[UIEditMenuInteraction _editMenuPresentation:didSelectMenuLeaf:completion:] + 316 3 UIKitCore 0x146c1d8 -[_UIEditMenuContentPresentation editMenuListView:didSelectMenuElement:] + 244 4 UIKitCore 0xc76bf4 -[_UIEditMenuListView _selectItemAtIndexPath:] + 280 ... 30 MyAppName 0x19430 main + 120 (AppDelegate.swift:120)
相关代码片段
var style = ""; Widget build(BuildContext context) { return BlocConsumer<myCubit, myState>( bloc: myCubit, listener: (context, state) { }, builder: (context, state) { return Scaffold( backgroundColor: context.theme.appColors.white, body: Listener( onPointerDown: gestureHandler.onPointerDown, onPointerMove: gestureHandler.onPointerMove, onPointerUp: gestureHandler.onPointerUp, behavior: HitTestBehavior.translucent, child: InAppWebView( gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{ Factory<LongPressGestureRecognizer>( () => LongPressGestureRecognizer(), ), }, contextMenu: _createContextMenu(), initialUrlRequest: URLRequest( url: WebUri("//$readerPath"), ), initialSettings: InAppWebViewSettings( javaScriptEnabled: true, disableContextMenu: false, // ... other settings ), onWebViewCreated: (controller) { myCubit.webViewController = controller; controller.setContextMenu(_createContextMenu()); }, ), ), ); }, ); } ContextMenu _createContextMenu() { return ContextMenu( menuItems: [ ContextMenuItem(id: 1, title: CONTEXT_MENU_1), // Other items... ], settings: ContextMenuSettings(hideDefaultSystemContextMenuItems: true), onCreateContextMenu: (hitTestResult) async { isContextMenuOpen.value = true; // Show custom menu... }, onContextMenuActionItemClicked: (menuItem) async { _menuItemClicked(menuItem.id); }, ); } void _menuItemClicked(int itemId) { try { switch (itemId) { case 1: style = InterfaceConst.OPTION_1; } } }
核心疑问
- 这个崩溃是否和iOS 18中
UIEditMenuInteraction的变化有关? - 如何安全地在
InAppWebView中覆盖或扩展iOS原生上下文菜单? - 使用
flutter_inappwebview时,同时使用原生+自定义菜单是否有已知问题或临时解决方案?
问题解答
1. 崩溃是否与iOS 18的UIEditMenuInteraction变化相关?
是的。iOS 18对UIEditMenuInteraction的内部回调逻辑、菜单选择后的事件传递流程做了不少底层调整。你使用的flutter_inappwebview 6.1.5是一个相对老旧的版本,没有适配iOS 18的这些新变化,导致原生层的UICommand performWithSender:target:调用与Flutter侧的手势识别、自定义菜单回调逻辑发生冲突——比如手势识别器拦截了原生菜单的事件,或者自定义菜单的回调没有正确绑定到原生层的处理逻辑,最终触发了异常调用(崩溃日志中的Missing帧也侧面说明原生符号与Flutter侧的交互出现了不兼容)。
2. 如何安全覆盖/扩展iOS原生上下文菜单?
可以从以下几个方面优化:
- 优先升级依赖版本:尽快升级
flutter_inappwebview到最新稳定版(比如v7.x系列,该系列已经针对iOS 18做了适配),新版本通常会修复这类系统版本适配问题。 - 避免手势识别器冲突:你自定义的
LongPressGestureRecognizer可能和iOS原生上下文菜单的长按触发逻辑冲突。可以尝试调整手势的延迟时间(比如LongPressGestureRecognizer(duration: const Duration(milliseconds: 500))),或者通过gestureRecognizers的配置,让原生菜单的长按事件优先被处理。 - 规范上下文菜单回调逻辑:确保
onCreateContextMenu和onContextMenuActionItemClicked中的操作是异步安全的,比如更新isContextMenuOpen这类状态时,要保证在Flutter UI线程执行;不要在回调中做阻塞UI的同步操作。 - 选择合适的菜单实现方式:如果需要完全自定义菜单,建议设置
disableContextMenu: true,直接使用Flutter原生的PopupMenu或自定义弹窗,彻底避免与iOS原生菜单的交互冲突;如果需要保留部分原生菜单功能,不要设置hideDefaultSystemContextMenuItems: true后又依赖原生菜单的未实现逻辑。
3. 已知问题与临时解决方案
- 已知问题:
flutter_inappwebview v6.x系列在iOS 18上,当同时配置自定义手势识别器和自定义上下文菜单时,容易出现事件传递冲突,触发原生层崩溃。 - 临时解决方案:
- 临时移除自定义的
LongPressGestureRecognizer,改用InAppWebView原生的长按触发逻辑,验证是否还会崩溃。如果问题消失,说明手势冲突是核心原因,再调整手势的优先级或范围。 - 暂时将
disableContextMenu设为true,完全切换到Flutter侧的自定义菜单,绕过原生菜单的适配问题。 - 在
_menuItemClicked方法中完善异常处理:当前的try块没有捕获任何异常类型,也没有catch分支,建议补充catch (e)逻辑,避免未处理的异常从Flutter侧渗透到原生层导致崩溃。 - 调整
Listener的HitTestBehavior:将translucent改为HitTestBehavior.deferToChild,确保InAppWebView能优先接收原生事件,避免事件被Listener拦截。
- 临时移除自定义的
内容来源于stack exchange
相关产品推荐
相关产品推荐

