Я пытаюсь создать форму во флаттере, в которой пользователь будет вводить различные данные о питательных веществах пищи, которую он или она съел / планирует съесть, после чего я покажу им, как будет оцениваться уровень сахара в крови после питание на основе расчета статистической модели. Но всякий раз, когда я пытаюсь отправить форму, я получаю эту ошибку. Несмотря на страницу, не используется никакое свойство переменной .length. Я в полной растерянности относительно того, почему это происходит.
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building FoodManualInput(dirty, dependencies: [MediaQuery], state: _FoodManualInputState#a02e9):
The getter 'length' was called on null.
Receiver: null
Tried calling: length
User-created ancestor of the error-causing widget was
MaterialApp
lib\main.dart:72
When the exception was thrown, this was the stack
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 double._parse (dart:core-patch/double_patch.dart:95:19)
Вот исходный код страницы: (Я удалил содержимое виджета Форма, потому что не смогу опубликуйте этот вопрос, если я этого не сделал)
class _FoodManualInputState extends State<FoodManualInput> {
final GlobalKey<FormState> _manualInputKey = GlobalKey<FormState>();
String _foodTypeInput, _noOfFood;
bool showComputation = false;
String _fname;
var _servSize,
_caloriesSize,
_carbServSz,
_sugarServSz,
_fatServSz,
_proteinServSz,
_estServing;
String numberValidator(String value) {
if (value == null) {
return null;
}
final n = num.tryParse(value);
if (n == null) {
return '"$value" is not a valid number';
}
return null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
child: Column(
children: <Widget>[
SizedBox(height: 20),
//manual appbar
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Add Food (Manual Input)",
textAlign: TextAlign.left,
style: TextStyle(
fontFamily: FintnessAppTheme.fontName,
fontWeight: FontWeight.w700,
fontSize: 16,
letterSpacing: 1.2,
color: FintnessAppTheme.darkerText,
),
),
),
),
Padding(
padding: const EdgeInsets.only(
left: 8,
),
child: Row(
children: <Widget>[],
),
),
SizedBox(
height: 38,
width: 38,
child: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.close),
color: FintnessAppTheme.grey,
highlightColor: Colors.transparent,
)),
],
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Text(
'If you\'re eating more than one item, kindly just add the nutrients in grams per field in the form. For example: i\'ll eat Frosties and Bread, I will add the carbs in Frosties, and in the Bread outside the application and input it in the respective formfield.',
textAlign: TextAlign.justify,
style: TextStyle(
fontFamily: FintnessAppTheme.fontName,
fontSize: 13,
letterSpacing: 1.2,
color: FintnessAppTheme.darkerText,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 460,
child: ListView(
children: <Widget>[
Center(
child: Container(
width: 200,
child: DropdownButton<String>(
hint: Text(
'Choose a meal type',
textAlign: TextAlign.center,
),
items: <String>[
'Snack',
'Breakfast',
'Lunch',
'Dinner'
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
value: _foodTypeInput,
onChanged: (newValue) {
setState(() {
_foodTypeInput = newValue;
});
},
),
),
),
SizedBox(height: 20),
Center(
child: Container(
width: 370,
child: DropdownButton<String>(
hint: Text(
'Choose a number of foods to manually compute'),
items:
<String>['1', '2', '3', '4'].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
value: _noOfFood,
onChanged: (newValue) {
setState(() {
_noOfFood = newValue;
});
},
),
),
),
_foodTypeInput == null || _noOfFood == null
? SizedBox()
: Form(RaisedButton(
onPressed: () {
doAction();
setState(() {
showComputation = true;
});
},
child: Text('Compute'),
color: Colors.pink[200],
),
)
],
),
),
],
),
),
),
Divider(
color: Colors.indigo,
thickness: 2,
),
showComputation == false
? SizedBox(
child: Text('showComputation val: $showComputation'),
)
: Container(
child: Column(
children: <Widget>[
Container(
child: Text(
'Total meal calories: ${calories().toString()} cal ',
style: FintnessAppTheme.title,
),
alignment: Alignment.bottomRight,
),
SizedBox(
height: 10,
),
Container(
alignment: Alignment.center,
child: Text(
'This is your estimated blood glucose after eating this meal: ',
style: FintnessAppTheme.title,
textAlign: TextAlign.center,
)),
Text(
'${compute()} mg/dL',
style: TextStyle(
fontFamily: FintnessAppTheme.fontName,
fontWeight: FontWeight.w600,
fontSize: 28,
color: FintnessAppTheme.nearlyDarkBlue,
),
),
SizedBox(
height: 40,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
RaisedButton(
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
FoodManualInput()));
},
child: Text('Search Again'),
),
RaisedButton(
onPressed: () {},
color: Colors.green,
child: Text('Add this food'),
),
],
)
],
),
)
],
),
),
);
}
Вот функции, которые я создал для вычислений:
void doAction() {
if (_manualInputKey.currentState.validate()) {
_manualInputKey.currentState.save();
}
}
double compute() {
//convert all captures user input to double to be able to compute
// carbs in nutrient table
double carbs = double.parse(_carbServSz);
print('$carbs, ${carbs.runtimeType}');
// fat in nutrient table
double fat = double.parse(_fatServSz);
print('$fat, ${fat.runtimeType}');
//protein in nutrient table
double protein = double.parse(_proteinServSz);
print('$protein, ${protein.runtimeType}');
// sugar in nutrient table
double sugar = double.parse(_sugarServSz);
print('$sugar, ${sugar.runtimeType}');
// serving size stated in nutrient table
double servSz = double.parse(_servSize);
print('$servSz, ${servSz.runtimeType}');
// estimated servings eaten by user
double userAte = double.parse(_estServing);
print('$userAte, ${userAte.runtimeType}');
double divisor = double.parse(_noOfFood);
//real computation
double estimatedBg = 0.0;
double bloodGlucoseIntercept = 215.3370423;
double totalCarbs = ((userAte / servSz) * carbs) / divisor;
print('this is totalCarbs : $totalCarbs');
estimatedBg =
(estimatedBg + bloodGlucoseIntercept) + (0.286275132 * totalCarbs);
showComputation = true;
return estimatedBg;
}
double calories() {
double carbs = double.parse(_carbServSz);
print('$carbs, ${carbs.runtimeType}');
// fat in nutrient table
double fat = double.parse(_fatServSz);
print('$fat, ${fat.runtimeType}');
//protein in nutrient table
double protein = double.parse(_proteinServSz);
print('$protein, ${protein.runtimeType}');
// sugar in nutrient table
double sugar = double.parse(_sugarServSz);
print('$sugar, ${sugar.runtimeType}');
// serving size stated in nutrient table
double servSz = double.parse(_servSize);
print('$servSz, ${servSz.runtimeType}');
// estimated servings eaten by user
double userAte = double.parse(_estServing);
print('$userAte, ${userAte.runtimeType}');
double divisor = double.parse(_noOfFood);
var totalCalories = ((_estServing / _servSize) * _caloriesSize) / divisor;
print('this is totalCalories : $totalCalories');
return totalCalories;
}
Вот как выглядит страница:
(showComputationVal = false - это логический флаг, показывающий, будет ли страница отображать вычисление или только размерный блок, если для него установлено значение true, он должен отображать вычисления, однако я просто поместил его в текстовый виджет, чтобы узнать, работает ли это условие в моем пользовательском интерфейсе правильно.)