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

Flutter中如何根据条件自动关闭Dialog对话框

问题根因

你当前的实现存在4个核心问题,导致弹窗无法自动关闭、逻辑运行异常:

  • 把网络监听逻辑写在StatelessWidget的build方法中,每次组件重建都会重复注册监听、重复触发弹窗,且StatelessWidget没有dispose生命周期,订阅无法正常取消会造成内存泄漏
  • 断网时仅调用了showDialog,网络恢复时没有主动触发弹窗关闭逻辑,且没有做弹窗防重复判断,会堆叠多个无网络弹窗
  • Provider中的_internetConnected字段没有设置初始值,首次调用时会抛出空异常
  • 调用subscription.cancel时漏写了执行括号,实际没有执行订阅取消操作

修正实现

第一步:修正网络状态Provider

给网络状态字段设置初始值,避免空异常:

import 'package:flutter/material.dart';

class InternetStatusProvider with ChangeNotifier {
  // 初始化默认网络状态为已连接,避免首次运行空错误
  bool _internetConnected = true;

  bool get internetStatus => _internetConnected;

  void setInternetStatus(bool internetStatus) {
    if (_internetConnected != internetStatus) {
      _internetConnected = internetStatus;
      notifyListeners();
    }
  }
}

第二步:将页面组件改为StatefulWidget,统一管理监听和弹窗状态

将网络监听初始化放到initState生命周期中,增加弹窗展示标记位避免重复弹窗,网络恢复时自动关闭已展示的弹窗:

// ignore_for_file: avoid_print, use_build_context_synchronously
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:providers_sample/widgets/second_widget.dart';
import '../providers/InternetStatusProvider.dart';

class SampleWidget extends StatefulWidget {
  const SampleWidget({Key? key}) : super(key: key);

  @override
  State<SampleWidget> createState() => _SampleWidgetState();
}

class _SampleWidgetState extends State<SampleWidget> {
  late StreamSubscription subscription;
  // 标记当前是否已展示无网络弹窗,避免重复弹出
  bool _isDialogShowing = false;

  @override
  void initState() {
    super.initState();
    // 初始化时注册网络监听,不要放到build方法中
    _initInternetListener();
  }

  void _initInternetListener() async {
    final internetStatusProvider = Provider.of<InternetStatusProvider>(context, listen: false);
    // 首次进入页面先获取一次当前网络状态
    final initialStatus = await Connectivity().checkConnectivity();
    _updateConnectionStatus(initialStatus, internetStatusProvider);
    // 持续监听后续网络状态变化
    subscription = Connectivity().onConnectivityChanged.listen((status) {
      _updateConnectionStatus(status, internetStatusProvider);
    });
  }

  void _updateConnectionStatus(
    ConnectivityResult result,
    InternetStatusProvider provider
  ) {
    final hasInternet = result == ConnectivityResult.mobile || result == ConnectivityResult.wifi;
    provider.setInternetStatus(hasInternet);
    print("当前网络连接状态: $hasInternet");

    if (!hasInternet) {
      // 断网且弹窗未展示时才触发弹窗
      if (!_isDialogShowing) {
        _isDialogShowing = true;
        _showNoInternetDialog();
      }
    } else {
      // 网络恢复且弹窗正在展示时,自动关闭弹窗
      if (_isDialogShowing) {
        _isDialogShowing = false;
        // 弹出栈顶的弹窗路由,不会影响页面正常路由
        Navigator.of(context).pop();
      }
    }
  }

  void _showNoInternetDialog() {
    showDialog(
      context: context,
      // 禁止点击弹窗外部手动关闭,仅允许网络恢复时自动关闭
      barrierDismissible: false,
      builder: (ctx) => AlertDialog(
        title: Text(
          "提示",
          style: TextStyle(color: Theme.of(context).primaryColor),
        ),
        content: const Text("当前无网络连接,请检查网络设置后重试"),
      ),
    );
  }

  @override
  void dispose() {
    // 补全执行括号,正确取消网络订阅
    subscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return const SecondWidget();
  }
}

额外注意点
  • 如果你使用3.0及以上版本的connectivity_plus,onConnectivityChanged的返回值为List<ConnectivityResult>类型,判断联网状态时需要检测列表中是否包含ConnectivityResult.mobile或ConnectivityResult.wifi即可
  • 如果需要允许用户手动关闭无网络弹窗,可以删除barrierDismissible: false配置,同时给弹窗加关闭按钮,按钮点击回调中同步把_isDialogShowing标记改回false,避免下次断网时弹窗无法正常弹出
  • connectivity_plus仅能检测设备是否连接到局域网/移动网络,无法检测网络是否能正常访问公网,如果需要判断真实可用网络,可以在检测到wifi/移动网络连接后,增加一步访问公网地址的连通性校验逻辑

内容的提问来源于stack exchange,提问作者Chirag Maurya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 03:48:22