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

Flutter仅点击重试按钮时重检网络连接的问题修复

修复网络状态Dialog不响应Retry问题及优化架构建议

核心问题拆解

当前问题的本质是:

  • Dialog的显示状态(isDialogDisplayed)未与ConnectivityCubit的状态绑定,导致状态变更无法被UI监听
  • 点击Retry按钮时,仅执行网络检查但未触发Cubit状态更新,UI层无法感知状态变化从而关闭Dialog

修复方案

1. 扩展ConnectivityCubit状态,纳入Dialog显示标记

修改Cubit的状态类,新增isDialogDisplayed字段,统一管理网络状态与Dialog显示状态:

class ConnectivityState {
  final ConnectivityResult networkStatus;
  final bool isDialogDisplayed;

  ConnectivityState({
    required this.networkStatus,
    required this.isDialogDisplayed,
  });

  ConnectivityState copyWith({
    ConnectivityResult? networkStatus,
    bool? isDialogDisplayed,
  }) {
    return ConnectivityState(
      networkStatus: networkStatus ?? this.networkStatus,
      isDialogDisplayed: isDialogDisplayed ?? this.isDialogDisplayed,
    );
  }
}

更新ConnectivityCubit,封装网络状态更新与Retry逻辑:

class ConnectivityCubit extends Cubit<ConnectivityState> {
  final Connectivity _connectivity = Connectivity();
  late StreamSubscription<ConnectivityResult> _connectivitySubscription;

  ConnectivityCubit() 
      : super(ConnectivityState(
          networkStatus: ConnectivityResult.none,
          isDialogDisplayed: false,
        )) {
    _initConnectivity();
    _connectivitySubscription = _connectivity.onConnectivityChanged.listen(_handleNetworkChange);
  }

  Future<void> _initConnectivity() async {
    final result = await _connectivity.checkConnectivity();
    emit(state.copyWith(networkStatus: result));
  }

  void _handleNetworkChange(ConnectivityResult result) async {
    // 加入真实网络可达性校验(比单纯的ConnectivityResult更可靠)
    final isReachable = await _checkNetworkReachability();
    final updatedStatus = isReachable ? result : ConnectivityResult.none;

    emit(state.copyWith(networkStatus: updatedStatus));
    
    // 网络恢复时自动关闭Dialog
    if (updatedStatus != ConnectivityResult.none && state.isDialogDisplayed) {
      emit(state.copyWith(isDialogDisplayed: false));
    }
  }

  // 处理Retry按钮点击
  void onRetryPressed() async {
    final isReachable = await _checkNetworkReachability();
    if (isReachable) {
      emit(state.copyWith(isDialogDisplayed: false));
    }
  }

  // 真实网络可达性检查(ping后端或公共域名)
  Future<bool> _checkNetworkReachability() async {
    try {
      final address = await InternetAddress.lookup('your-backend-domain.com');
      return address.isNotEmpty && address[0].rawAddress.isNotEmpty;
    } on SocketException {
      return false;
    }
  }

  @override
  Future<void> close() {
    _connectivitySubscription.cancel();
    return super.close();
  }
}

2. UI层通过BlocListener联动Dialog

在根Widget使用BlocListener监听Cubit状态变化,处理Dialog的弹出与关闭:

void main() {
  runApp(
    BlocProvider(
      create: (context) => ConnectivityCubit(),
      child: const MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocListener<ConnectivityCubit, ConnectivityState>(
        listener: (context, state) {
          // 无网络且未显示Dialog时弹出
          if (state.networkStatus == ConnectivityResult.none && !state.isDialogDisplayed) {
            showDialog(
              context: context,
              barrierDismissible: false,
              builder: (_) => AlertDialog(
                title: const Text("无网络连接"),
                content: const Text("请检查网络设置后重试"),
                actions: [
                  TextButton(
                    onPressed: () => context.read<ConnectivityCubit>().onRetryPressed(),
                    child: const Text("重试"),
                  ),
                ],
              ),
            );
            // 更新Cubit状态标记Dialog已显示
            context.read<ConnectivityCubit>().emit(state.copyWith(isDialogDisplayed: true));
          }

          // 网络恢复且Dialog已显示时关闭
          if (state.networkStatus != ConnectivityResult.none && state.isDialogDisplayed) {
            Navigator.of(context).pop();
          }
        },
        child: const HomeScreen(),
      ),
    );
  }
}

更优架构建议

  1. 分离业务与UI逻辑:所有网络状态判断、Dialog显示逻辑都封装在Cubit中,UI层仅负责响应状态变化,避免业务逻辑散落在UI代码里
  2. 强制真实网络校验:不要仅依赖Connectivity插件的结果,加入域名ping校验,避免“有连接但无互联网”的场景
  3. 状态防抖处理:网络状态可能频繁切换(比如WiFi信号波动),加入1-2秒的防抖定时器,避免Dialog频繁弹出/关闭
  4. 全局统一管理:将网络状态监听放在应用根节点,确保所有页面都能统一响应网络变化,无需重复实现
  5. 封装状态修改方法:在Cubit中封装showNetworkDialog()、hideNetworkDialog()等方法,禁止UI层直接调用emit,保证状态修改的可控性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 20:47:45