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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 22:02:29