Flutter Bloc状态仅首次更新 加载指示器重复触发失效问题
问题根因
加载指示器仅首次点击生效,是三处代码逻辑错误共同导致的:
- 违反flutter_bloc的状态设计原则:flutter_bloc要求状态为不可变对象,原有代码直接修改现有state实例的
loadingState属性,属于可变状态操作;且连续emit同一引用的状态时,bloc内部会拦截重复的状态更新,不会触发UI重建。 - 业务状态类实现缺陷:
HomeState构造函数没有向父类BaseState传递loadingState参数,所有新创建的HomeState实例的loadingState永远是默认值initial;同时copyWith方法没有覆盖loadingState字段,业务逻辑更新count时生成的新状态会丢失加载状态。 - 视图层逻辑错误:
BaseView中存在多余的错误BlocProvider,上层已经通过MultiBlocProvider注入了HomeCubit,BaseView的build方法重复创建了类型不匹配的BaseCubit实例,造成上下文cubit引用混乱。
修复方案
- 重构状态基类与业务状态类:所有状态属性声明为
final保证不可变,子类构造函数正确传递父类字段,copyWith方法覆盖全量字段(包括父类的加载状态字段)。 - 改造
BaseCubit的通用请求方法:禁止直接修改现有状态属性,所有状态更新都通过copyWith生成新实例后再调用emit。 - 清理视图层冗余代码:删除
BaseView中多余的BlocProvider,直接使用上层注入的cubit实例。
修复后完整代码
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider(create: (_) => HomeCubit()), ], child: const MaterialApp( title: 'Flutter Bloc Demo', home: HomeView(), ), ); } } class HomeView extends BaseView<HomeCubit, HomeState> { const HomeView({Key? key}) : super(key: key); @override Widget builder(HomeCubit cubit, HomeState state) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(state.count.toString()), ElevatedButton( onPressed: cubit.increment, child: const Text('Increase'), ), ], ), ); } } // 状态基类 enum LoadingState { initial, loading, loaded } abstract class BaseState { const BaseState({ this.loadingState = LoadingState.initial, }); final LoadingState loadingState; BaseState copyWith({LoadingState? loadingState}); } // 业务状态类 class HomeState extends BaseState { final int count; const HomeState({ required this.count, super.loadingState, }); @override HomeState copyWith({ int? count, LoadingState? loadingState, }) { return HomeState( count: count ?? this.count, loadingState: loadingState ?? this.loadingState, ); } } // 业务Cubit基类 class BaseCubit<S extends BaseState> extends Cubit<S> { BaseCubit(S state) : super(state); Future<void> flow(Future<void> Function() function) async { // 生成新的加载状态实例,禁止修改原状态 emit(state.copyWith(loadingState: LoadingState.loading) as S); await function(); // 生成新的加载完成状态实例 emit(state.copyWith(loadingState: LoadingState.loaded) as S); } } class HomeCubit extends BaseCubit<HomeState> { HomeCubit() : super(const HomeState(count: 0)); void increment() { flow(() async { await Future.delayed(const Duration(seconds: 1)); emit(state.copyWith(count: state.count + 1)); }); } } // 视图基类 @immutable abstract class BaseView<C extends StateStreamable<S>, S extends BaseState> extends StatelessWidget { const BaseView({ Key? key, }) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: BlocBuilder<C, S>( builder: (context, state) { final cubit = context.read<C>(); if (state.loadingState == LoadingState.loading) { return loadingWidget; } return builder.call(cubit, state); }, ), ); } Widget builder(C cubit, S state); Widget get loadingWidget => const Center( child: CircularProgressIndicator(), ); }
内容的提问来源于stack exchange,提问作者Lacrimalis
相关产品推荐
相关产品推荐

