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

Flutter整洁架构+Cubit下实现网络状态监听流的方案咨询

问题描述

我基于**Clean Architecture(整洁架构)**和Cubit搭建了Flutter项目,在core/network目录下实现了NetworkInfo类,通过Future方法检测网络连接,目前在RepositoryImpl中使用正常。但我需要添加网络状态监听stream以持续检测网络状态,不清楚应在哪个层级编写代码,以及如何在不违反整洁架构原则、保证可测试性的前提下结合Cubit使用streams?

现有代码

NetworkInfo 抽象类与实现

abstract class NetworkInfo {
  Future<bool> isConnected();
}

class NetworkInfoImpl implements NetworkInfo {
  InternetConnection internetConnection;

  NetworkInfoImpl({
    required this.internetConnection,
  });

  @override
  Future<bool> isConnected() {
    // TODO: implement isConnected
    return internetConnection.hasInternetAccess;
  }
}

AuthRepositoryImpl

class AuthRepositoryImpl implements AuthRepository {
  final AuthRemoteDatasource authRemoteDatasource;
  final NetworkInfo networkInfo;

  AuthRepositoryImpl({
    required this.authRemoteDatasource,
    required this.networkInfo,
    required this.crashLog,
  });

  @override
  ResultFuture<User> emailPasswordLogin({
    required String email,
    required String password,
  }) async {
    // TODO: implement emailPasswordLogin
    try {
      if (!await networkInfo.isConnected()) {
        return const Result.error(NoInternetFailure());
      }

      var user = await authRemoteDatasource.emailPasswordLogin(
        email: email,
        password: password,
      );

      return Result.success(user);
    } on AuthException {
      return const Result.error(AuthFailure());
    } catch (exception, stack) {
      return Result.error(UnknownFailure());
    }
  }
}

解决方案

1. 扩展NetworkInfo抽象层(核心层)

按照整洁架构的要求,抽象定义必须放在核心层。我们给NetworkInfo添加网络状态监听的stream接口,这样既保持核心层的抽象性,又能让实现类提供具体的流能力,不依赖外部库。

修改后的抽象类:

abstract class NetworkInfo {
  Future<bool> isConnected();
  // 添加网络状态变化的stream接口
  Stream<bool> get connectionStatusStream;
}

2. 在NetworkInfoImpl中实现Stream逻辑

在core/network的实现类里,基于你使用的InternetConnection库实现stream。如果库本身不支持监听,也可以用定期检测的方式生成stream:

class NetworkInfoImpl implements NetworkInfo {
  final InternetConnection internetConnection;
  // 可选:自定义检测周期
  final Duration checkInterval;

  NetworkInfoImpl({
    required this.internetConnection,
    this.checkInterval = const Duration(seconds: 5),
  });

  @override
  Future<bool> isConnected() {
    return internetConnection.hasInternetAccess;
  }

  @override
  Stream<bool> get connectionStatusStream {
    // 方式1:如果库自带监听stream,直接返回
    // return internetConnection.connectionStatusStream;
    
    // 方式2:定期检测实现(适配无自带stream的库)
    return Stream.periodic(checkInterval, (_) async => await isConnected())
        .asyncMap((future) => future)
        // 过滤重复状态,避免频繁触发UI更新
        .distinct();
  }
}

3. 分层职责划分:Stream处理放在Presentation层

网络状态属于全局UI状态(比如显示离线提示),不属于Repository的业务逻辑范畴。根据整洁架构,持续状态监听应该交给Presentation层的Cubit管理,Repository只负责单次业务操作前的网络检测。

4. 实现NetworkCubit(Presentation层)

创建专门的NetworkCubit订阅网络状态stream,统一管理网络状态并通知UI,既符合单一职责,也方便测试:

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import '../../core/network/network_info.dart';

// 网络状态实体类
class NetworkState extends Equatable {
  final bool isConnected;

  const NetworkState({required this.isConnected});

  @override
  List<Object> get props => [isConnected];
}

class NetworkCubit extends Cubit<NetworkState> {
  final NetworkInfo networkInfo;
  StreamSubscription? _networkSubscription;

  NetworkCubit({required this.networkInfo}) : super(const NetworkState(isConnected: false)) {
    _startListening();
  }

  void _startListening() {
    _networkSubscription = networkInfo.connectionStatusStream.listen((isConnected) {
      emit(NetworkState(isConnected: isConnected));
    });
  }

  // 手动触发一次网络检测(比如UI主动刷新)
  Future<void> checkConnection() async {
    final isConnected = await networkInfo.isConnected();
    emit(NetworkState(isConnected: isConnected));
  }

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

5. UI层使用NetworkCubit

通过BlocListener或BlocBuilder监听状态变化,实现UI层面的离线提示等逻辑:

BlocListener<NetworkCubit, NetworkState>(
  listener: (context, state) {
    if (!state.isConnected) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text("当前无网络连接")),
      );
    }
  },
  child: // 你的UI内容
)

6. 保证可测试性的关键

  • 始终依赖NetworkInfo抽象类,测试时可以mock这个类,返回自定义的stream来模拟网络状态变化。
  • 测试NetworkCubit时,通过mockNetworkInfo的connectionStatusStream,验证Cubit是否正确emit对应状态。
  • Repository层保持原有逻辑不变,只负责单次网络检测,不需要处理stream,避免职责混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 04:13:23