I’m trying to update state after creating an Object.
I’ve created ObjectsState using freezed
package, so there is no sence to use Equatable, because it is already add by package.
class ObjectsState with _$ObjectsState {
const factory ObjectsState.initial() = Initial;
const factory ObjectsState.loading() = _Loading;
const factory ObjectsState.loaded(ObjectsListResponse response) = Loaded;
const factory ObjectsState.error(String message) = _Error;
}
then, this is my event for fetching objects
@freezed
class ObjectsEvent with _$ObjectsEvent {
const factory ObjectsEvent.onFetchObjects() = _OnFetchObjects;
}
but I also have CreateObjectEvent
separately.
And my bloc file:
class ObjectsBloc extends Bloc<ObjectsEvent, ObjectsState> {
final ObjectsUseCase objectsUseCase;
ObjectsBloc({required this.objectsUseCase})
: super(const ObjectsState.initial()) {
on<ObjectsEvent>((event, emit) => event.when(
onFetchObjects: () => _fetchObjects(emit),
));
}
Future<void> _fetchObjects(Emitter<ObjectsState> emit) async {
emit(const ObjectsState.loading());
try {
final response = await objectsUseCase.executeFetchObjects();
if (response.isError || response.result == null) {
emit(ObjectsState.error(
response.errorMessage ?? 'Failed to fetch objects'));
} else {
final updatedResponse = response.result!.copyWith(
objectsList: List.from(response.result!.objectsList),
total: response.result!.total,
);
final loadedState = ObjectsState.loaded(updatedResponse);
final updatedState = (loadedState as Loaded).copyWith(
response: updatedResponse,
);
emit(updatedState);
}
} catch (e) {
emit(ObjectsState.error(e.toString()));
}
}
}
so, on the stage emit(updatedState);
updatedState has right value, but when it goes to the UI part – nothing is updated
class ObjectsScreenWrapper extends StatefulWidget {
ObjectsScreenWrapper({super.key});
@override
State<ObjectsScreenWrapper> createState() => _ObjectsScreenWrapperState();
}
class _ObjectsScreenWrapperState extends State<ObjectsScreenWrapper> {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<ObjectsBloc>(
create: (_) =>
ObjectsBloc(
objectsUseCase: GetIt.I<ObjectsUseCase>(),
),
),
BlocProvider<BalanceBloc>(
create: (_) =>
BalanceBloc(
balanceUseCase: GetIt.I<BalanceUseCase>(),
),
),
],
child: ObjectsScreen(),
);
}
}
class ObjectsScreen extends StatelessWidget {
ObjectsScreen({super.key});
@override
Widget build(BuildContext context) {
if (context.read<ObjectsBloc>().state is Initial) {
context.read<ObjectsBloc>().add(const ObjectsEvent.onFetchObjects());
}
BlocBuilder<ObjectsBloc, ObjectsState>(
key: ValueKey(context.watch<ObjectsBloc>().state),
builder: (context, state) {
return state.when(
initial: ...
loading: ...
loaded: (objectsListResponse) {
if (objectsListResponse.total <= 0) {
return ... ;
} else {
return ObjectsLayoutGrid(
key: ValueKey(objectsListResponse
.copyWith()
.objectsList
.hashCode),
I’ve found a lot of related issues, but nothing worked for me. If anyone know how to fix this, please help
2
Answers
As per my knowledge, When we emit the same state with Freezed, the UI doesn’t rebuild because Freezed compares the states and skips the rebuild if they are equal, so to ensure the UI rebuilds, you can add a field that changes with every emit like timestamp as below:
Modify Your State
Add a DateTime or int like below field to your state:
Emit Updated State
When emitting the new state, include an updated timestamp:
First of all, do not run this in the build method
instead put it in the provider create method
or use initState of a stateful widget because it is possible for that to run multiple times before the state changes to loading if you keep it in the build method.
Remove the
key
you gave to theBlocBuilder
, idk why would you need to do that.The rest of the code seems fine and it should be rebuilding the ui.
It could be that the code has not been updated on the app, running a
flutter clean
can fix some issues related to code not being updated on the app