Flutter食谱APP开发:如何在BlocBuilder外访问Cubit State?
当前开发食谱APP时,使用BlocBuilder包裹界面内容,但FloatingActionButton在BlocBuilder外部,无法直接访问Cubit的状态来获取食谱列表的lastID,代码如下:
class _HomeScreenState extends State<HomeScreen> { @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text("The Recipe Bloc"), backgroundColor: Colors.orange[800], ), body: Center(child: BlocBuilder<RecipeCubit, RecipeState>( builder: (context, state) { if (state is RecipeInitial) { return const CircularProgressIndicator(color: Colors.orange); } if (state is RecipeLoaded) { return state.recipeList.isEmpty ? const Center(child: Text("No Recipes Registered Yet!")) : ListView.builder( itemCount: state.recipeList.length, itemBuilder: (context, index) { return RecipeListTile(parentContext: context, recipeItem: state.recipeList[index]).display; }); } else { return const Text("Something Went Wrong!"); } }, )), floatingActionButton: FloatingActionButton( backgroundColor: Colors.orange[800], onPressed: () { // 此处无法访问BlocBuilder内的state showDialog(context: context, builder: (BuildContext context) => CreateRecipeDialog(lastID: state.recipeList.isNotEmpty ? state.recipeList.last.id : "-1")); }, child: const Icon(Icons.add_rounded)), ); } }
解决方案
方法1:直接通过Context读取Cubit状态
利用context.read<T>()获取已注入的Cubit实例,直接访问其state属性,判断状态类型后提取所需数据:
修改FloatingActionButton的onPressed逻辑:
floatingActionButton: FloatingActionButton( backgroundColor: Colors.orange[800], onPressed: () { // 获取Cubit实例并读取当前状态 final recipeState = context.read<RecipeCubit>().state; String lastID = "-1"; // 仅当状态为RecipeLoaded且列表非空时更新lastID if (recipeState is RecipeLoaded && recipeState.recipeList.isNotEmpty) { lastID = recipeState.recipeList.last.id; } showDialog( context: context, builder: (context) => CreateRecipeDialog(lastID: lastID), ); }, child: const Icon(Icons.add_rounded), ),
方法2:使用BlocSelector监听目标字段
如果仅需要lastID而非整个状态,用BlocSelector可以只监听目标字段的变化,减少不必要的重建,同时直接拿到所需值:
将整个Scaffold包裹在BlocSelector中:
@override Widget build(BuildContext context) { return BlocSelector<RecipeCubit, RecipeState, String>( // 只提取需要的lastID字段 selector: (state) { if (state is RecipeLoaded && state.recipeList.isNotEmpty) { return state.recipeList.last.id; } return "-1"; }, builder: (context, lastID) { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text("The Recipe Bloc"), backgroundColor: Colors.orange[800], ), body: Center(child: BlocBuilder<RecipeCubit, RecipeState>( builder: (context, state) { // 原BlocBuilder逻辑不变 if (state is RecipeInitial) { return const CircularProgressIndicator(color: Colors.orange); } if (state is RecipeLoaded) { return state.recipeList.isEmpty ? const Center(child: Text("No Recipes Registered Yet!")) : ListView.builder( itemCount: state.recipeList.length, itemBuilder: (context, index) { return RecipeListTile(parentContext: context, recipeItem: state.recipeList[index]).display; }); } else { return const Text("Something Went Wrong!"); } }, )), floatingActionButton: FloatingActionButton( backgroundColor: Colors.orange[800], onPressed: () { // 直接使用BlocSelector传递的lastID showDialog( context: context, builder: (context) => CreateRecipeDialog(lastID: lastID), ); }, child: const Icon(Icons.add_rounded), ), ); }, ); }
方法3:在State类中持有Cubit实例
在initState中获取Cubit实例并保存到State变量,后续直接访问其状态,适合多次需要调用Cubit的场景:
class _HomeScreenState extends State<HomeScreen> { late RecipeCubit _recipeCubit; @override void initState() { super.initState(); // 初始化时获取Cubit实例 _recipeCubit = context.read<RecipeCubit>(); } @override Widget build(BuildContext context) { return Scaffold( // 原appBar、body逻辑不变 floatingActionButton: FloatingActionButton( backgroundColor: Colors.orange[800], onPressed: () { final state = _recipeCubit.state; String lastID = "-1"; if (state is RecipeLoaded && state.recipeList.isNotEmpty) { lastID = state.recipeList.last.id; } showDialog( context: context, builder: (context) => CreateRecipeDialog(lastID: lastID), ); }, child: const Icon(Icons.add_rounded), ), ); } }
内容的提问来源于stack exchange,提问作者David Black
相关产品推荐
相关产品推荐

