Flutter 网络恢复时自动重载Bloc轮播组件未加载数据方案
实现方案
你现有项目已经集成了connectivity_wrapper,无需额外引入依赖即可完成网络状态监听和自动重载逻辑,核心改动点如下:
- 在组件状态类中新增网络状态流订阅实例、数据加载成功标记位
- 初始化时订阅网络变化事件,仅当网络从断开恢复连接、且轮播数据未加载成功时,主动触发数据重载
- 监听Bloc状态变化同步加载标记,避免数据加载完成后重复触发无效请求
- 组件销毁时注销网络订阅,避免内存泄漏
- 优化错误状态UI,避免纯空白无提示的问题
具体代码改动
1. 修改状态类的生命周期和变量定义
class _MonkCarouselState extends State<MonkCarousel> { // 网络状态变化订阅 late StreamSubscription _connectivitySub; // 标记轮播数据是否已加载成功 bool _dataLoaded = false; _loadMonks() async { if (await ConnectivityWrapper.instance.isConnected) { context.read<LatestMonkBloc>().add(const GetLastMonksEvent(5)); } } @override void initState() { super.initState(); _loadMonks(); // 监听网络状态变更 _connectivitySub = ConnectivityWrapper.instance.onConnectivityChanged.listen((connected) { // 网络恢复且数据未加载完成时自动重试 if (connected && !_dataLoaded) { _loadMonks(); } }); } @override void dispose() { // 注销订阅防止内存泄漏 _connectivitySub.cancel(); super.dispose(); }
2. 用BlocListener包裹原有BlocBuilder,同步加载状态
把原有BlocBuilder外层嵌套BlocListener,用来监听Bloc状态更新加载标记,同时优化错误状态的UI展示:
// 替换原有child位置的BlocBuilder代码 child: BlocListener<LatestMonkBloc, LatestMonkState>( listener: (context, state) { _dataLoaded = state is LatestMonkLoaded; }, child: BlocBuilder<LatestMonkBloc, LatestMonkState>( builder: (context, state) { if (state is LatestMonkError) { return Container( width: MediaQuery.of(context).size.width, margin: const EdgeInsets.symmetric(horizontal: 10), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).scaffoldBackgroundColor, borderRadius: BorderRadius.circular(20), ), child: const Center( child: Text("数据加载失败,网络恢复后将自动重试"), ), ); } else if (state is LatestMonkLoaded) { // 原有CarouselSlider逻辑完全保留,不需要修改 return CarouselSlider( options: CarouselOptions( height: 160, autoPlay: true, autoPlayCurve: Curves.fastOutSlowIn, enableInfiniteScroll: true, autoPlayAnimationDuration: const Duration(milliseconds: 800), viewportFraction: 0.5, ), items: state.monks.map((monk) { return Builder( builder: (BuildContext context) { return GestureDetector( onTap: () { pushNewScreen(context, screen: SongScreen( monk: monk, album: Album(id: monk.id, monkId: monk.id, title: kLatestDhama), screenMode: SongScreenMode.latest, ), pageTransitionAnimation: PageTransitionAnimation.scale); }, child: Container( width: MediaQuery.of(context).size.width, margin: const EdgeInsets.symmetric(horizontal: 10), padding: const EdgeInsets.only(left: 5, right: 5, top: 0), decoration: BoxDecoration( color: Theme.of(context).scaffoldBackgroundColor, borderRadius: BorderRadius.circular(20), ), child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ CachedNetworkImage( width: 100.0, height: 100.0, imageUrl: monk.imageUrl, imageBuilder: (context, imageProvider) => Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), image: DecorationImage( image: imageProvider, fit: BoxFit.cover, ), ), ), placeholder: (context, url) => Container(color: Colors.black12), errorWidget: (context, url, error) => const Icon( Icons.error, size: 100, color: Colors.red, ), ), const SizedBox(height: 5), Flexible( child: AutoSizeText( monk.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 15), ), ), ], ), ), ); }, ); }).toList(), ); } return const CircularProgressIndicatorWidget(); }, ), ),
注意事项
- 如果你当前使用的
connectivity_wrapper版本中,onConnectivityChanged返回的是ConnectivityResult枚举而非bool值,只需要把判断条件从if (connected && !_dataLoaded)改成if (result != ConnectivityResult.none && !_dataLoaded)即可,逻辑完全一致 - 加载标记
_dataLoaded会在数据加载成功时自动置为true,避免网络频繁切换时重复发起已经成功的接口请求,减少资源消耗 - 错误状态增加文字提示后,用户不会看到无反馈的空白区块,交互体验更友好
内容的提问来源于stack exchange,提问作者Alex Aung
相关产品推荐
相关产品推荐

