I created simple app to test bloc 7.2.0 and faced that BlocBuilder doesn't rebuild after first successful rebuild. On every other trigger bloc emits new state, but BlocBuilder ignores it.
Please note, if I remove extends Equatable
and its override from both, state and event, then BlocBuilder rebuilds UI every time Button pressed. Flutter version 2.5.1
If Equatable is necessary, why it's not working with it? If Equatable isn't necessary, why it's been used in initial creation via VSCode extension.
My code:
bloc part
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
//bloc
class MainBloc extends Bloc<MainEvent, MainState> {
MainBloc() : super(MainInitial()) {
on<MainButtonPressedEvent>(_onMainButtonPressedEvent);
}
void _onMainButtonPressedEvent(
MainButtonPressedEvent event, Emitter<MainState> emit) {
emit(MainCalculatedState(event.inputText));
}
}
//states
abstract class MainState extends Equatable {
const MainState();
@override
List<Object> get props => [];
}
class MainInitial extends MainState {}
class MainCalculatedState extends MainState {
final String exportText;
const MainCalculatedState(this.exportText);
}
//events
abstract class MainEvent extends Equatable {
const MainEvent();
@override
List<Object> get props => [];
}
class MainButtonPressedEvent extends MainEvent {
final String inputText;
const MainButtonPressedEvent(this.inputText);
}
UI part
import 'package:bloc_test/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: BlocProvider(
create: (context) => MainBloc(),
child: SubWidget(),
),
),
);
}
}
class SubWidget extends StatelessWidget {
TextEditingController inputText = TextEditingController();
String? exportText;
@override
Widget build(BuildContext context) {
MainBloc mainBloc = BlocProvider.of<MainBloc>(context);
return BlocBuilder<MainBloc, MainState>(
builder: (context, state) {
if (state is MainCalculatedState) {
exportText = state.exportText;
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${exportText ?? ''} data'),
SizedBox(
width: 200,
child: TextField(
controller: inputText,
),
),
ElevatedButton(
onPressed: () =>
mainBloc.add(MainButtonPressedEvent(inputText.text)),
child: const Text('Button')),
],
),
);
},
);
}
}
CodePudding user response:
Equatable is used to make it easy for you to program, how and when states are the same (no update) and when they are different (update).
Your updates do not work because you are sending the same state repeatedly, but you did not tell the Equatable extension how to find out if they are different. So they are all the same.
So to make sure your program understands that some states of the same kind are indeed different and should cause an update, you need to make sure you mention what makes them different:
class MainCalculatedState extends MainState {
final String exportText;
const MainCalculatedState(this.exportText);
// this tells the Equatable base class to consider your text property
// when trying to figure out if two states are different.
// If the text is the same, the states are the same, so no update
// If the text is different, the states are different, so it will update
@override
List<Object> get props => [this.exportText];
}
If you remove Equatable altogether, two newly instanciated states are never equal, so that would solve your problem as well... except that at some point you will want them to be, and then you need to add it back in.
CodePudding user response:
Your MainCalculatedState
needs to override the props
getter from Equatable
and return the list of all properties which should be used to assess equality. In your case it should return [exportText]
.
Example:
class MainCalculatedState extends MainState {
final String exportText;
const MainCalculatedState(this.exportText);
@override
List<Object> get props => [exportText];
}