Flutter:如何复用同一页面并注入不同Bloc实现适配
复用多媒体网格页面并注入不同Bloc的实现方案
核心思路是通过抽象Bloc层让页面依赖抽象而非具体实现,再在不同入口处注入对应的具体Bloc,实现页面复用同时适配不同业务场景。
步骤1:抽象Bloc、事件与状态
先定义通用的抽象类,统一多媒体场景的事件和状态接口,让GroupBloc和PostBloc都遵循这个规范:
抽象事件类
abstract class MediaEvent {} // 通用上传事件 class MediaUploadEvent extends MediaEvent { final List<File> mediaFiles; MediaUploadEvent(this.mediaFiles); } // 通用加载多媒体列表事件 class MediaLoadEvent extends MediaEvent {}
抽象状态类
abstract class MediaState {} class MediaLoadingState extends MediaState {} class MediaLoadedState extends MediaState { final List<Media> mediaList; MediaLoadedState(this.mediaList); } class MediaUploadSuccessState extends MediaState {} class MediaErrorState extends MediaState { final String errorMsg; MediaErrorState(this.errorMsg); }
抽象Bloc类
abstract class MediaBloc extends Bloc<MediaEvent, MediaState> { MediaBloc() : super(MediaLoadingState()) { on<MediaLoadEvent>(_onLoadMedia); on<MediaUploadEvent>(_onUploadMedia); } // 子类必须实现的加载逻辑 void _onLoadMedia(MediaLoadEvent event, Emitter<MediaState> emit); // 子类必须实现的上传逻辑 void _onUploadMedia(MediaUploadEvent event, Emitter<MediaState> emit); }
步骤2:实现具体业务Bloc
分别为群组和帖子场景实现具体的Bloc,继承抽象的MediaBloc:
GroupBloc实现
class GroupBloc extends MediaBloc { final Group group; GroupBloc(this.group); @override void _onLoadMedia(MediaLoadEvent event, Emitter<MediaState> emit) async { // 调用群组多媒体加载接口 try { final mediaList = await GroupApi.getGroupMedia(group.id); emit(MediaLoadedState(mediaList)); } catch (e) { emit(MediaErrorState(e.toString())); } } @override void _onUploadMedia(MediaUploadEvent event, Emitter<MediaState> emit) async { // 调用群组多媒体上传接口 try { await GroupApi.uploadGroupMedia(group.id, event.mediaFiles); emit(MediaUploadSuccessState()); add(MediaLoadEvent()); // 上传成功后重新加载列表 } catch (e) { emit(MediaErrorState(e.toString())); } } }
PostBloc实现
class PostBloc extends MediaBloc { final Post post; PostBloc(this.post); @override void _onLoadMedia(MediaLoadEvent event, Emitter<MediaState> emit) async { // 调用帖子多媒体加载接口 try { final mediaList = await PostApi.getPostMedia(post.id); emit(MediaLoadedState(mediaList)); } catch (e) { emit(MediaErrorState(e.toString())); } } @override void _onUploadMedia(MediaUploadEvent event, Emitter<MediaState> emit) async { // 调用帖子多媒体上传接口 try { await PostApi.uploadPostMedia(post.id, event.mediaFiles); emit(MediaUploadSuccessState()); add(MediaLoadEvent()); // 上传成功后重新加载列表 } catch (e) { emit(MediaErrorState(e.toString())); } } }
步骤3:编写复用的多媒体网格页面
页面中依赖抽象的MediaBloc,不直接引用具体Bloc,这样就能适配两种场景:
class MediaGridView extends StatelessWidget { const MediaGridView({super.key}); @override Widget build(BuildContext context) { final mediaBloc = context.read<MediaBloc>(); return Scaffold( appBar: AppBar(title: const Text("多媒体网格")), body: BlocBuilder<MediaBloc, MediaState>( builder: (context, state) { if (state is MediaLoadingState) { return const Center(child: CircularProgressIndicator()); } else if (state is MediaLoadedState) { return GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 4, mainAxisSpacing: 4, ), itemCount: state.mediaList.length, itemBuilder: (context, index) { final media = state.mediaList[index]; return media.isImage ? Image.network(media.url) : VideoPlayerWidget(url: media.url); }, ); } else if (state is MediaErrorState) { return Center(child: Text(state.errorMsg)); } return const SizedBox(); }, ), floatingActionButton: FloatingActionButton( onPressed: () async { // 选择图片/视频逻辑 final files = await pickMedia(); if (files.isNotEmpty) { mediaBloc.add(MediaUploadEvent(files)); } }, child: const Icon(Icons.add), ), ); } // 模拟媒体选择方法 Future<List<File>> pickMedia() async { // 实现图片/视频选择逻辑 return []; } }
步骤4:在不同入口注入具体Bloc
从群组页面跳转时,注入GroupBloc;从帖子页面跳转时,注入PostBloc:
群组入口跳转
// 群组页面中跳转至多媒体网格页 Navigator.push( context, MaterialPageRoute( builder: (context) => BlocProvider( create: (context) => GroupBloc(currentGroup)..add(MediaLoadEvent()), child: const MediaGridView(), ), ), );
帖子入口跳转
// 帖子页面中跳转至多媒体网格页 Navigator.push( context, MaterialPageRoute( builder: (context) => BlocProvider( create: (context) => PostBloc(currentPost)..add(MediaLoadEvent()), child: const MediaGridView(), ), ), );
这样就能实现同一页面组件在不同场景下,自动使用对应的Bloc处理业务逻辑,并且能根据对应Bloc的状态自动重建UI。
内容的提问来源于stack exchange,提问作者kk-at-cbx
相关产品推荐
相关产品推荐

