Flutter中WebViewScaffold断网检测弹窗失效问题求助
在WebViewScaffold中实现网络连接检测与断网提示对话框
我之前也碰到过类似的问题,普通WebView里的网络监听逻辑放到WebViewScaffold里失效,大概率是context的获取时机不对或者对话框的上下文不匹配导致的。下面是我整理的可行解决方案,你可以直接参考修改:
第一步:确认依赖配置
首先确保你的pubspec.yaml里已经添加了必要的依赖:
dependencies: flutter: sdk: flutter webview_flutter: ^4.4.0 # 使用最新稳定版即可 connectivity_plus: ^5.0.1 # 替代旧的connectivity包,功能更全面
第二步:修改网络监听与对话框逻辑
核心是解决context的问题,同时优化监听时机避免异常。下面是完整的代码示例:
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'dart:io'; class WebViewPage extends StatefulWidget { const WebViewPage({Key? key}) : super(key: key); @override State<WebViewPage> createState() => _WebViewPageState(); } class _WebViewPageState extends State<WebViewPage> { late StreamSubscription<ConnectivityResult> connectivitySubscription; ConnectivityResult? _previousResult; bool _dialogShown = false; final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); // 检查真实网络连通性(不仅仅是设备连接状态) Future<bool> _checkInternet() async { try { final result = await InternetAddress.lookup('google.com'); return result.isNotEmpty && result[0].rawAddress.isNotEmpty; } on SocketException catch (_) { return false; } } // 弹出断网提示对话框 void _showNoInternetDialog() { // 确保Widget已挂载且对话框未重复显示 if (!_dialogShown && mounted) { _dialogShown = true; showDialog( context: _scaffoldKey.currentContext!, barrierDismissible: false, builder: (context) => AlertDialog( title: const Text("Error"), content: const Text("No Data Connection Available."), actions: <Widget>[ TextButton( onPressed: () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const InternetConnectionPage()), ); }, child: const Text("Exit"), ), ], ), ); } } // 处理网络状态变化 void _handleNetworkChange(ConnectivityResult result) async { if (result == ConnectivityResult.none) { _showNoInternetDialog(); } else if ((result == ConnectivityResult.mobile || result == ConnectivityResult.wifi) && _previousResult == ConnectivityResult.none) { // 恢复网络时,先确认真的能访问互联网 final hasRealInternet = await _checkInternet(); if (hasRealInternet && _dialogShown && mounted) { _dialogShown = false; Navigator.pop(_scaffoldKey.currentContext!); // 可选:重新加载WebView内容 // _webViewController.reload(); } } _previousResult = result; } @override void initState() { super.initState(); // 延迟初始化监听,确保WebViewScaffold已经挂载完成,context可用 WidgetsBinding.instance.addPostFrameCallback((_) { connectivitySubscription = Connectivity().onConnectivityChanged.listen(_handleNetworkChange); }); } @override void dispose() { // 页面销毁时取消监听,避免内存泄漏 connectivitySubscription.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return WebViewScaffold( key: _scaffoldKey, // 绑定GlobalKey获取正确的上下文 url: "https://your-target-url.com", // 替换成你的实际URL appBar: AppBar(title: const Text("Web View")), withZoom: true, withLocalStorage: true, hidden: true, initialChild: const Center(child: CircularProgressIndicator()), ); } } // 示例的断网跳转页面,替换成你自己的页面即可 class InternetConnectionPage extends StatelessWidget { const InternetConnectionPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("No Internet")), body: const Center( child: Text("Please check your internet connection and try again."), ), ); } }
关键优化点说明
- GlobalKey获取上下文:用
GlobalKey<ScaffoldState>绑定WebViewScaffold,确保弹出对话框时使用的是WebViewScaffold的有效上下文,避免普通context可能带来的异常。 - 延迟初始化监听:通过
WidgetsBinding.instance.addPostFrameCallback在Widget完全挂载后再启动网络监听,解决initState阶段context未就绪的问题。 - 真实网络校验:Connectivity包只能检测设备是否连接到网络,不能确认是否能访问互联网,所以恢复网络时要额外调用
_checkInternet验证。 - 挂载状态检查:每次操作前检查
mounted,避免Widget销毁后调用setState或弹出对话框导致崩溃。
内容的提问来源于stack exchange,提问作者muhammad alshelleh
相关产品推荐
相关产品推荐

