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
相关产品推荐
相关产品推荐

