Flutter中SingleChildScrollView内嵌InAppWebView崩溃及滚动交互实现需求
问题根因
Android 系统对 WebView 最大渲染高度存在16384px的硬限制,你当前通过JS获取内容高度动态修改WebView容器高度的方案,在内容过长时就会超出限制触发IllegalStateException异常。
修复方案
1. 替换动态高度为固定视口高度
废弃动态计算WebView高度的逻辑,直接给WebView设置固定高度,推荐取值为屏幕高度的80%~100%,保证渲染高度不会超出系统阈值:
// 提前获取当前屏幕高度 final screenHeight = MediaQuery.of(context).size.height; // WebView容器配置 SizedBox( // 固定高度,可根据业务场景调整比例 height: screenHeight * 0.9, child: InAppWebview( // 开启WebView自身滚动能力 scrollBarEnabled: true, // 注册垂直拖拽手势识别,保证WebView优先消费滚动事件 gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{ Factory<VerticalDragGestureRecognizer>(() => VerticalDragGestureRecognizer()), }, // 剩余原有配置保留即可 ), )
删除原有onLoadStop中修改_webviewHeightSetInOnLoadStop的逻辑。
2. 实现滚动边界事件透传
通过监听WebView滚动位置,在到达上下边界时将滚动事件透传给外层SingleChildScrollView处理:
- 首先给外层
SingleChildScrollView绑定滚动控制器:
final _outerScrollController = ScrollController(); // 外层滚动容器配置 SingleChildScrollView( controller: _outerScrollController, // 原有配置保留即可 )
- 然后在
InAppWebview的onScrollChanged回调中处理边界逻辑:
onScrollChanged: (controller, x, y) async { // 获取WebView内容总高度和可视高度 final scrollHeight = await controller.evaluateJavascript( source: "document.documentElement.scrollHeight;" ); final clientHeight = await controller.evaluateJavascript( source: "document.documentElement.clientHeight;" ); // 滚动到顶部,透传向下拖拽事件给外层滚动容器 if (y <= 0) { _outerScrollController.position.jumpTo(_outerScrollController.offset + y); } // 滚动到底部,透传向上拖拽事件给外层滚动容器 if (y + clientHeight >= scrollHeight) { final overScrollDelta = y + clientHeight - scrollHeight; _outerScrollController.position.jumpTo(_outerScrollController.offset + overScrollDelta); } }
3. 可选优化:消除滚动冲突回弹
可以关闭WebView自身的越界回弹效果,避免和外层滚动的回弹产生冲突:
InAppWebView( initialOptions: InAppWebViewGroupOptions( android: AndroidInAppWebViewOptions( overScrollMode: AndroidOverScrollMode.OVER_SCROLL_NEVER, ), ios: IOSInAppWebViewOptions( bounces: false, ), ), // 原有配置保留即可 )
内容的提问来源于stack exchange,提问作者Stoyan Milev
相关产品推荐
相关产品推荐

