I have a late property(_bmi
) set to null by default and then I initialize it in a method calcBMI()
, after its initialization, I use its value in two more methods, result
and interpretation
to fulfill some bool conditions, but I run into a lateinitialisationerror
, because the first method (result()
) doesn't recognize the value I initialized in calcBMI()
, it rather defaults to the initial null value that the _bmi
property had, however the interpretation()
method recognizes the initialized value. How I know it's only the first method that doesn't recognize it is coz when I pass a value to the _bmi
property when I'm setting it, the app doesn't throw the error, it works, but then it uses the set value for the first method, but the initialised value from calcBMI()
for the second method, what am I doing wrong, here is the code.
class CalculatorBrain {
CalculatorBrain({required this.weight, required this.height});
int weight;
int height;
late double _bmi = 30; // when I set this value, result() works with it, but interpretation() works with the value passed in calcBMI();
String calcBMI() {
_bmi = weight / pow(height / 100, 2);
return _bmi.toStringAsFixed(1);
}
String result() {
if(_bmi >= 25) {
return 'overweight';
} else if(_bmi > 18.5) {
return 'normal';
} else {
return 'underweight';
}
}
String interpretation() {
if(_bmi >= 25) {
return 'You have a higher than normal body weight. Try to exercise more.';
} else if(_bmi > 18.5) {
return 'You have a normal body weight. Good job.';
} else {
return 'You have a lower than normal body weight. You can eat a bit more.';
}
}
}
this is where I use my class
BottomButton(
onTap: () {
CalculatorBrain calc = CalculatorBrain(
weight: weight,
height: height,
);
setState(() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ResultsPage(result: calc.result(), bMI: calc.calcBMI(), interpretation: calc.interpretation());
}
),
);
});
},
title: 'CALCULATE',
),
CodePudding user response:
If you got a LateInitializationError
before, it's not that "[result()
] doesn't recognize the value I initialized in calcBMI()
", it's that you called result()
before you called calcBMI()
.
Giving _bmi
an initial value avoids the LateInitializationError
, but you still have the same fundamental problem: you're reading _bmi
before you call calcBMI()
to assign it the value you actually want.
In particular, you have:
return ResultsPage(result: calc.result(), bMI: calc.calcBMI(), interpretation: calc.interpretation());
Dart evaluates function arguments in left-to-right order, so you'll call calc.result()
first, then calc.calcBMI()
, and then calc.interpretation()
. Changing the order should fix your problem:
return ResultsPage(
bMI: calc.calcBMI(),
result: calc.result(),
interpretation: calc.interpretation(),
);
However, relying on argument evaluation order would be poor style. It would not be obvious to readers (including your future self) that argument order matters. It would be much better to explicitly order operations that are order-dependent:
var bmi = calc.calcBMI();
return ResultsPage(
result: calc.result(),
bMI: bmi,
interpretation: calc.interpretation(),
);
Note that this has nothing to do with _bmi
being late
. Declaring _bmi
as late
provides no purpose in your code as currently written, and you could just remove it. But you also should consider rewriting your code to make CalculatorBrain
less dependent on consumers calling its methods in a particular order. Some possibilities:
Compute _bmi
dynamically
You could make _bmi
a getter that computes the correct value for weight
/height
on every access:
class CalculatorBrain {
CalculatorBrain({required this.weight, required this.height});
int weight;
int height;
double get _bmi => weight / pow(height / 100, 2);
String calcBMI() => _bmi.toStringAsFixed(1);
String result() {
final _bmi = this._bmi;
if(_bmi >= 25) {
return 'overweight';
} else if(_bmi > 18.5) {
return 'normal';
} else {
return 'underweight';
}
}
...
Compute _bmi
exactly once
If you make weight
/height
final
, then you can compute _bmi
once and be done:
class CalculatorBrain {
CalculatorBrain({required this.weight, required this.height});
final int weight;
final int height;
// `late` computes `_bmi` lazily, which is necessary because it depends on
// `weight` and `height`.
late final double _bmi = weight / pow(height / 100, 2);
...
Update _bmi
automatically if weight
or height
changes
If you weight
/height
must be mutable, then you could create setters so that _bmi
is always updated automatically:
class CalculatorBrain {
CalculatorBrain({required int weight, required int height})
: _weight = weight,
_height = height {
_updateBmi();
}
late double _bmi;
int _weight;
int get weight => _weight;
set weight(int value) {
_weight = value;
_updateBmi();
}
int _height;
int get height => _height;
set height(int value) {
_height = value;
_updateBmi();
}
void _updateBmi() {
_bmi => weight / pow(height / 100, 2);
}
...
CodePudding user response:
late modifier means that you will at some point define it, which you are doing in calcBMI()
, after which you call interpretation()
where _bmi
already has a value.
Your code would crash if you just call result()
since _bmi
still isn't defined.