So I currently have 2 screens, one screen has a button and when the user presses the button, second screen gets displayed. Nothing too complicated...
The second screen should react to what the Bloc sent and essentially display the data from the state being emitted by the Bloc.
Everything was working fine and the vairables fixedExpenseTotal
and variableExpenseTtoal
were being displayed correctly until I enhanced the Bloc to emit an extra state; FetchedBalanceBoughtForwardState
.
Now it seems like the the vairables fixedExpenseTotal
and variableExpenseTtoal
are not being populated, it's displaying empty strings. What it seems like is that the widget is not able to react to the states initially, when I make the Bloc re-emit the states (using a temporary button on screen 2), the widgets on screen 2 reacts correctly. So i'm confused as to why the widgets on screen 2 does not react when the widget gets loaded.... is it because the states are being emitted before the widget is loaded? Is it worth just adding the balance
field as part of the state in FetchedExpendituresForCertainMonthState
to reduce the complexity (although i would like states/events to be as loosely coupled as posible)? Although having placed print
statements, i can see initially when the screen 2 is loaded, it does ingest the FetchedBalanceBoughtForwardState
but not the FetchedExpendituresForCertainMonthState
.
This is my Bloc:
on<FetchForMonthExpenditureDefinitions>((event, emit) async {
//...business logic here
emit(FetchedExpendituresForCertainMonthState(
expendituresWithinTheMonth, monthToTraverse));
//Now we want to calculate the balance being bought forward from the previous month
add(CalculateBalanceBoughtForwardEvent(expenditures, from));
});
on<CalculateBalanceBoughtForwardEvent>((event, emit) async {
double balance = calculator.calculateBalanceBeingBoughtForward(event.expenditureDefinitions, event.dateOfBalanceBoughtForward);
print('Sending the balance bought forward as $balance');
emit(FetchedBalanceBoughtForwardState(balance));
});
and this is the onTap handler of the button in screen 1:
TextButton(
child: const Text('Finish!'),
onPressed: () async {
//Passing the context which contains the blocprovider to myApp
context.read<FinanceBloc>().add(
FetchForMonthExpenditureDefinitions(
DateTime.now()));
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<FinanceBloc>(context),
child: MyApp(),
),
));
},
)
And this is the widget that is reacting to the states being emited by the Bloc on screen 2:
return Container(
width: MediaQuery.sizeOf(context).width,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
color: Colors.grey,
),
child: BlocBuilder<FinanceBloc, FinanceState>(
buildWhen: (previous, current) =>
previous != current && current is FetchedBalanceBoughtForwardState,
builder: (context, balanceState) {
double balanceBeingBoughtForward = balanceState is FetchedBalanceBoughtForwardState
? balanceState.balanceBoughtForward
: 0.0;
return BlocBuilder<FinanceBloc, FinanceState>(
buildWhen: (previous, current) =>
previous != current && current is FetchedExpendituresForCertainMonthState,
builder: (context, expendituresState) {
String fixedIncomeTotal = "";
String fixedExpenseTotal = "";
String variableExpenseTtoal = "";
String variableIncomeTotla = "";
if (expendituresState is FetchedExpendituresForCertainMonthState) {
expenditures = expendituresState.expenditures.values
.expand((list) => list)
.toList();
currentMonth = expendituresState.updatedDate;
fixedIncomeTotal = getFixedIncome(expenditures).toString();
fixedExpenseTotal = getFixedExpense(expenditures).toString();
variableExpenseTtoal = getVariableExpense(expenditures).toString();
variableIncomeTotla = getVariableIncome(expenditures).toString();
}
return Column(children: [
Text("Overview of expenditures for this month",
style: const TextStyle(fontSize: 24)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Total fixed income: £$fixedIncomeTotal"),
Text("Total fixed expense: £$fixedExpenseTotal")
]
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Total variable income: £$variableIncomeTotla"),
Text("Total variable expense: £$variableExpenseTtoal")
]
),
Text("Balance bought forward: £$balanceBeingBoughtForward"),
Text("Click here for more intel!"),
]);
},
);
},
),
),
);
So I currently have 2 screens, one screen has a button and when the user presses the button, second screen gets displayed. Nothing too complicated...
The second screen should react to what the Bloc sent and essentially display the data from the state being emitted by the Bloc.
Everything was working fine and the vairables fixedExpenseTotal
and variableExpenseTtoal
were being displayed correctly until I enhanced the Bloc to emit an extra state; FetchedBalanceBoughtForwardState
.
Now it seems like the the vairables fixedExpenseTotal
and variableExpenseTtoal
are not being populated, it's displaying empty strings. What it seems like is that the widget is not able to react to the states initially, when I make the Bloc re-emit the states (using a temporary button on screen 2), the widgets on screen 2 reacts correctly. So i'm confused as to why the widgets on screen 2 does not react when the widget gets loaded.... is it because the states are being emitted before the widget is loaded? Is it worth just adding the balance
field as part of the state in FetchedExpendituresForCertainMonthState
to reduce the complexity (although i would like states/events to be as loosely coupled as posible)? Although having placed print
statements, i can see initially when the screen 2 is loaded, it does ingest the FetchedBalanceBoughtForwardState
but not the FetchedExpendituresForCertainMonthState
.
This is my Bloc:
on<FetchForMonthExpenditureDefinitions>((event, emit) async {
//...business logic here
emit(FetchedExpendituresForCertainMonthState(
expendituresWithinTheMonth, monthToTraverse));
//Now we want to calculate the balance being bought forward from the previous month
add(CalculateBalanceBoughtForwardEvent(expenditures, from));
});
on<CalculateBalanceBoughtForwardEvent>((event, emit) async {
double balance = calculator.calculateBalanceBeingBoughtForward(event.expenditureDefinitions, event.dateOfBalanceBoughtForward);
print('Sending the balance bought forward as $balance');
emit(FetchedBalanceBoughtForwardState(balance));
});
and this is the onTap handler of the button in screen 1:
TextButton(
child: const Text('Finish!'),
onPressed: () async {
//Passing the context which contains the blocprovider to myApp
context.read<FinanceBloc>().add(
FetchForMonthExpenditureDefinitions(
DateTime.now()));
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<FinanceBloc>(context),
child: MyApp(),
),
));
},
)
And this is the widget that is reacting to the states being emited by the Bloc on screen 2:
return Container(
width: MediaQuery.sizeOf(context).width,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
color: Colors.grey,
),
child: BlocBuilder<FinanceBloc, FinanceState>(
buildWhen: (previous, current) =>
previous != current && current is FetchedBalanceBoughtForwardState,
builder: (context, balanceState) {
double balanceBeingBoughtForward = balanceState is FetchedBalanceBoughtForwardState
? balanceState.balanceBoughtForward
: 0.0;
return BlocBuilder<FinanceBloc, FinanceState>(
buildWhen: (previous, current) =>
previous != current && current is FetchedExpendituresForCertainMonthState,
builder: (context, expendituresState) {
String fixedIncomeTotal = "";
String fixedExpenseTotal = "";
String variableExpenseTtoal = "";
String variableIncomeTotla = "";
if (expendituresState is FetchedExpendituresForCertainMonthState) {
expenditures = expendituresState.expenditures.values
.expand((list) => list)
.toList();
currentMonth = expendituresState.updatedDate;
fixedIncomeTotal = getFixedIncome(expenditures).toString();
fixedExpenseTotal = getFixedExpense(expenditures).toString();
variableExpenseTtoal = getVariableExpense(expenditures).toString();
variableIncomeTotla = getVariableIncome(expenditures).toString();
}
return Column(children: [
Text("Overview of expenditures for this month",
style: const TextStyle(fontSize: 24)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Total fixed income: £$fixedIncomeTotal"),
Text("Total fixed expense: £$fixedExpenseTotal")
]
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Total variable income: £$variableIncomeTotla"),
Text("Total variable expense: £$variableExpenseTtoal")
]
),
Text("Balance bought forward: £$balanceBeingBoughtForward"),
Text("Click here for more intel!"),
]);
},
);
},
),
),
);
When you did emit(FetchedBalanceBoughtForwardState(balance))
, The previously emitted state FetchedExpendituresForCertainMonthState
is lost. Since the BlocBuilder which listens to FetchedBalanceBoughtForwardState
is the parent, it triggers a rebuild which will also trigger rebuild of the child bloc so now the if condition expendituresState is FetchedExpendituresForCertainMonthState
is false
so the values don't get populated.
The best solution would be using just one type of state and use copyWith
to emit the new state so that the previously emitted state values are not lost. Also you should try to avoid nesting BlocBuilder
wherever you can as it may give unexpected results.
Consider using freezed for Bloc State. It will generate copyWith
method for you and also let you set the state back to null
. Although, you can use your own copyWith
implementation.