I have a problem with TextFormField initial values. It all works well until I activate (start typing) the TextFormField, then I get the error (type 'int' is not a subtype of type 'String?') and my Recipe id is reset to null. Disclaimer: All data map variables are of type String or List. Thanks in advance!
class NutritionScreen extends StatefulWidget {
static const routeName = '/route-name';
@override
_NutritionScreenState createState() => _NutritionScreenState();
}
class _NutritionScreenState extends State<NutritionScreen> {
final _formKey = GlobalKey<FormState>();
bool isLoading = false;
late RecipeProvider functions;
Map<String, dynamic> data = {};
Recipe newRecipe = Recipe(
title: '',
description: '',
id: '',
ingredients: [''],
steps: [''],
creatorId: '',
kcal: 0,
p: 0,
c: 0,
f: 0,
servings: 0,
);
@override
initState() {
super.initState();
Future.delayed(Duration.zero, () {
Map<String, dynamic> otherData =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
data = otherData;
functions = Provider.of<RecipeProvider>(context, listen: false);
});
}
Future<void> saveForm() async {
String userId = FirebaseAuth.instance.currentUser!.uid;
bool isValid = _formKey.currentState!.validate();
setState(() {
isLoading = true;
});
if (isValid == true) {
try {
final storagePath = FirebaseStorage.instance
.ref()
.child('recipe_image')
.child(userId '.jpg');
await storagePath.putFile(data['image']);
final imageUrl = await storagePath.getDownloadURL();
newRecipe.imageUrl = imageUrl;
_formKey.currentState!.save();
if (data['id'] == '') {
await functions.addRecipe(newRecipe);
} else {
await functions.editRecipe(newRecipe);
}
Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
} catch (error) {
rethrow;
}
}
setState(() {
isLoading = false;
});
}
@override
Widget build(BuildContext context) {
print(data['id']);
Size deviceSize = MediaQuery.of(context).size;
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text(
'Add Nutrition Data',
style: Theme.of(context).textTheme.headline1,
),
),
body: isLoading == true
? const Center(
child: CircularProgressIndicator(
semanticsLabel: 'Loading',
),
)
: Form(
key: _formKey,
child: ListView(
children: [
const SizedBox(
height: 20,
),
NutritionRow(
'Calories',
TextFormField(
initialValue: data['id'] == ''
? '0'
: data['kcal'],
validator: (value) {
if (int.parse(value!) <= 0) {
return 'Please enter a valid number';
} else if (value.isEmpty) {
return 'Please enter a number';
}
return null;
},
keyboardType: TextInputType.number,
decoration: const InputDecoration(labelText: 'kcal'),
onSaved: (value) {
newRecipe = Recipe(
title: data['title'],
description: data['description'],
id: newRecipe.id,
ingredients: data['ingredients'],
steps: data['steps'],
imageUrl: data['imageUrl'],
creatorId: data['creatorId'],
kcal: int.parse(value!),
p: newRecipe.p,
c: newRecipe.c,
f: newRecipe.f,
servings: newRecipe.servings,
);
},
),
),
NutritionRow(
'Protein',
TextFormField(
initialValue:
data['id'] == '' ? '0' : data['p'],
validator: (value) {
if (int.parse(value!) < 0) {
return 'Please enter a valid number';
} else if (value.isEmpty) {
return 'Please enter a number';
}
return null;
},
keyboardType: TextInputType.number,
decoration: const InputDecoration(labelText: 'protein'),
onSaved: (value) {
newRecipe = Recipe(
creatorId: data['creatorId'],
title: data['title'],
description: data['description'],
id: newRecipe.id,
ingredients: data['ingredients'],
steps: data['steps'],
imageUrl: data['imageUrl'],
kcal: newRecipe.kcal,
p: int.parse(value!),
c: newRecipe.c,
f: newRecipe.f,
servings: newRecipe.servings,
);
},
),
),
NutritionRow(
'Carbohydrates',
TextFormField(
initialValue:
data['id'] == '' ? '0' : data['c'],
validator: (value) {
if (int.parse(value!) < 0) {
return 'Please enter a valid number';
} else if (value.isEmpty) {
return 'Please enter a number';
}
return null;
},
keyboardType: TextInputType.number,
decoration:
const InputDecoration(labelText: 'Carbohydrates'),
onSaved: (value) {
newRecipe = Recipe(
creatorId: data['creatorId'],
title: data['title'],
description: data['description'],
id: newRecipe.id,
ingredients: data['ingredients'],
steps: data['steps'],
imageUrl: data['imageUrl'],
kcal: newRecipe.kcal,
p: newRecipe.p,
c: int.parse(value!),
f: newRecipe.f,
servings: newRecipe.servings,
);
},
),
),
NutritionRow(
'Fats',
TextFormField(
initialValue:
data['id'] == '' ? '0' : data['f'],
validator: (value) {
if (int.parse(value!) < 0) {
return 'Please enter a valid number';
} else if (value.isEmpty) {
return 'Please enter a number';
}
return null;
},
keyboardType: TextInputType.number,
decoration: const InputDecoration(labelText: 'Fats'),
onSaved: (value) {
newRecipe = Recipe(
creatorId: data['creatorId'],
title: data['title'],
description: data['description'],
id: newRecipe.id,
ingredients: data['ingredients'],
steps: data['steps'],
imageUrl: data['imageUrl'],
kcal: newRecipe.kcal,
p: newRecipe.p,
c: newRecipe.c,
f: int.parse(value!),
servings: newRecipe.servings,
);
},
),
),
NutritionRow(
'Servings',
TextFormField(
initialValue: data['id'] == '' ? '0' : data['servings'],
validator: (value) {
if (int.parse(value!) < 0) {
return 'Please enter a valid number';
} else if (value.isEmpty) {
return 'Please enter a number';
}
return null;
},
keyboardType: TextInputType.number,
decoration: const InputDecoration(labelText: 'Servings'),
onSaved: (value) {
newRecipe = Recipe(
title: data['title'],
description: data['description'],
id: newRecipe.id,
ingredients: data['ingredients'],
imageUrl: data['imageUrl'],
steps: data['steps'],
creatorId: data['creatorId'],
kcal: newRecipe.kcal,
p: newRecipe.p,
c: newRecipe.c,
f: newRecipe.f,
servings: int.parse(value!),
);
},
),
),
Container(
height: 50,
),
ElevatedButton(
style: Theme.of(context)
.elevatedButtonTheme
.style!
.copyWith(
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(
color: Theme.of(context).primaryColor,
),
),
),
fixedSize: MaterialStateProperty.all<Size>(
Size(
deviceSize.width * 0.6,
deviceSize.height * 0.07,
),
),
),
onPressed: saveForm,
child: Text(
'DONE',
style: Theme.of(context)
.textTheme
.headline2!
.copyWith(fontSize: 20),
),
),
const SizedBox(
height: 10,
),
],
),
),
);
}
}
class NutritionRow extends StatelessWidget {
final String value;
Widget child;
NutritionRow(this.value, this.child);
@override
Widget build(BuildContext context) {
return Column(
children: [
Card(
color: Colors.white,
elevation: 20,
margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.1,
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
value,
style: Theme.of(context)
.textTheme
.headline5!
.copyWith(fontSize: 20),
),
Expanded(
child: Container(),
),
SizedBox(
child: child,
width: 100,
),
],
),
),
),
const SizedBox(
height: 20,
),
],
);
}
}
CodePudding user response:
You cant set a value of TextField to an int but what you can do is that you can set the initial value as an string to the text field
TextFormField(
initialValue: data['id'] == ''
? '0'
: '${data['kcal']}',
validator: (value) {
if (int.parse(value!) <= 0) {
return 'Please enter a valid number';
} else if (value.isEmpty) {
return 'Please enter a number';
}
return null;
},
keyboardType: TextInputType.number,
decoration: const InputDecoration(labelText: 'kcal'),
onSaved: (value) {
newRecipe = Recipe(
title: data['title'],
description: data['description'],
id: newRecipe.id,
ingredients: data['ingredients'],
steps: data['steps'],
imageUrl: data['imageUrl'],
creatorId: data['creatorId'],
kcal: int.parse(value!),
p: newRecipe.p,
c: newRecipe.c,
f: newRecipe.f,
servings: newRecipe.servings,
);
},
),
then parse it as an int if you want an output of int
CodePudding user response:
you are trying to assign an integer to a string.
To fix this do this wherever needed:
initialValue: data['id'] == ''
? '0'
: data['kcal'].toString(),
You have this error because seem's to be an integer and you try to affect this integer to an string value.
toString method will convert you int to String.