Hello, im facing an issue, I read a lot but still did not find the solution.
I have a widget, which when I press it does a state change, in which case the widget rebuilds correctly.
But when I need to make an event change from somewhere else in the app, I see the event change in the log, but the widget doesn't rebuild.
This change of state is made by a class that listens to data from a bluetooth controller, so the change could occur at any time.
This is my widget
`
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:movekomapp/Utils/MyColors.dart';
import 'package:movekomapp/blocs/agua_blocs/bomba_agua_bloc.dart';
import 'package:movekomapp/responsive_ui/mi_positioned.dart';
import 'package:movekomapp/widgets/MyTextStyle.dart';
class BombaAgua extends StatelessWidget {
int widgetType;
String title;
BombaAgua(this.widgetType);
BombaAguaBloc bomabaAguaBloc;
@override
Widget build(BuildContext context) {
title = "Water Bomb";
bomabaAguaBloc = BlocProvider.of
return WaterBomb_principal();
}
Widget WaterBomb_principal() {
String txtState;
return
BlocBuilder
builder: (context, state) {
if (state.isEnabled) {
txtState = "ON";
} else {
txtState = "OFF";
}
return GestureDetector(
onTap: (){
if (state.isEnabled) {
bomabaAguaBloc.add(DisableBomba());
} else {
bomabaAguaBloc.add(EnableBomba());
}
},
child: Container(
margin: EdgeInsets.all(SC.all(7)),
width: 130,
height: 130,
decoration: new BoxDecoration(
color: MyColors.baseColor
),
child: Stack(
children: [
MyPositioned.fill(/// OFF / ON
left: 10,
bottom: 4,
child: Align(
alignment: Alignment.bottomLeft,
child: RichText(
text: TextSpan(
children: [
TextSpan(
style: MyTextStyle.estilo(
14, colorIcon),
text: txtState),
]
)
),
),
),
])
),
);
}
);
}
}`
This is the bloc
`import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:movekomapp/MapeoIDs/IdComponents.dart';
import 'package:movekomapp/bluetooth/BluetoothRepository.dart';
abstract class BombaAguaEvent extends Equatable {
BombaAguaEvent([List props = const []]) : super(props);
}
class EnableBomba extends BombaAguaEvent { /// Habilita la bateria
bool isEnabled = true;
@override
List
class DisableBomba extends BombaAguaEvent { /// Deshabilita la bateria
bool isEnabled = false;
@override
List
class BombaAguaState extends Equatable {
final bool isEnabled;
double valueAmp;
BombaAguaState({
@required this.isEnabled,
@required this.valueAmp,
}) : super([isEnabled,valueAmp]);
/// Valores iniciales
factory BombaAguaState.initial() {
return BombaAguaState(
isEnabled: true,
valueAmp: 2.65,
);
}
factory BombaAguaState.disabled() {
return BombaAguaState(
isEnabled: false,
valueAmp: 0.0,
);
}
BombaAguaState copyWith({
bool isEnabled,
double valueAmp,
}) {
return BombaAguaState(
isEnabled: isEnabled ?? this.isEnabled,
valueAmp: valueAmp ?? this.valueAmp,
);
}
@override
List
class BombaAguaBloc extends Bloc
var ID = IdComponent.BOMBA_AGUA;
@override
// TODO: implement initialState
BombaAguaState get initialState => BombaAguaState.initial();
@override
Stream
if (event is EnableBomba) {
yield* _setValuesAndSendBT(true);
// yield* _setInitValuesAndSendBT(event);
} else if (event is DisableBomba) {
yield* _setValuesAndSendBT(false);
}
}
Stream
print("_setInitValuesAndSendBT");
List
elems = generateData(status);
try {
BluetoothRepository.sendData(elems);
if (status)
yield BombaAguaState.initial();
else
yield BombaAguaState.disabled();
} catch(e) {
print(e);
}
}
}`
And this is the place where i need to dispatch an event, in log im seeing the state changes but the widget is not rebuilding, how can i notify the widget that need to be rebuild.
pd: i canot use context in this class because it is out fo the tree of widgets.
`import 'package:movekomapp/blocs/agua_blocs/bomba_agua_bloc.dart';
class BackgroundDataProcessor {
BackgroundDataProcessor() {}
void executeAction(){
print("executeAction");
BombaAguaBloc bomabaAguaBloc = BombaAguaBloc();
bomabaAguaBloc.add(DisableBomba());
bomabaAguaBloc.close();
}
}`
Hi @MeliGualaX 馃憢
Thanks for opening an issue!
Several suggestions for things to improve are:
Avoid maintaining state inside of a StatelessWidget's build. In your case txtState is a mutable variable being recreated each time the widget is rebuilt. You should probably remove that variable altogether and just use the bloc's state inside of the BlocBuilder directly.
Your event and state classes should all be immutable meaning all member variables should be final.
You need to inject the same bloc instance into the BackgrounDataProcessor. The current approach won't work because you're creating and adding events to a totally different instance of BombaAguaBloc and the UI is reacting to state changes in a separate instance. You should inject the same instance via the constructor of BackgroundDataProcess instead of creating one internally.
Hope that helps 馃憤
Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation. Also please check out the core concepts if you haven't already 馃憤
Hi @MeliGualaX 馃憢
Besides things pointed out by @felangel I will also suggest to move business logic out of UI and into bloc:
onTap: (){
if (state.isEnabled) {
bomabaAguaBloc.add(DisableBomba());
} else {
bomabaAguaBloc.add(EnableBomba());
}
You could just add a single event here and process it inside bloc where you already have access to isEnabled.