I am trying to allow inline editing… so have multiple fields where they are loaded as text widgets, when you click one it turns to a form field, and when you hit the enter key it commits and the text widget displays. There are other charts that will refresh based on the new values.
It is working OK except for two things.
- I’m not sure how to dispose the text controller.
- The validator I am passing to the buildFormField function isn’t being checked upon user hitting entry key (field is saved). I’d like to not be able to exit edit mode. In the gif it saves "abcd" to 0 instead of objecting…
Here is the function:
Widget buildFormField({
required String fieldName,
required String fieldValue,
required Function(String) onSubmitted,
required String? Function(String?) validator,
required TextInputType keyboardType,
required bool isEditing,
required VoidCallback onTap,
}) {
final TextEditingController controller = TextEditingController();
controller.text = fieldValue.replaceAll('$', '').replaceAll(',', '');
return GestureDetector(
onTap: onTap,
child: isEditing
? TextFormField(
controller: controller,
autofocus: true,
onFieldSubmitted: onSubmitted,
keyboardType: keyboardType,
decoration: InputDecoration(labelText: fieldName),
validator: validator,
autovalidateMode: AutovalidateMode.onUserInteraction,
)
: Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Container(
alignment: Alignment.centerLeft,
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: '$fieldName: ',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
TextSpan(
text: fieldValue,
style: const TextStyle(
fontSize: 16,
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.dashed,
),
),
],
),
),
),
),
);
}
Here is the function call…
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(
children: [
...
buildFormField(
fieldName: 'Purchase Price',
keyboardType: TextInputType.number,
fieldValue: Formatters.moneyFmt(
widget.property.purchasePrice),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a value';
}
final parsedVal = double.tryParse(value);
if (parsedVal == null || parsedVal <= 0) {
return 'Please enter a valid positive number';
}
return null;
},
onSubmitted: (value) {
final newProp = widget.property.copyWith(
purchasePrice:
double.tryParse(value) ?? 0.0);
context
.read<EditPropertyPageCubit>()
.update(newProp, widget.loans);
setState(() {
selectedField = null;
});
},
isEditing: selectedField == 'purchasePrice',
onTap: () {
setState(() {
selectedField =
'purchasePrice'; // Set the selected field
});
},
),...
2
Answers
First sorry if my english is confuse.
After reading your code I would like give to you some tips and solutions:
Do not use a method to build your widgets, you should use a class that extends stateless or statefull widget.
If you create a statefull widget you should be able to dispose a text controller, your parent widget does not need rebuild, use set state in the widget form that you create.
Your form field display when you tap becouse you do not using validator condition:
if(_formKey.currentState!.validate()) { setState(() {
isEditing = false
});}
instead of add validator for non-digit characters, try to set
inputFormater
keyboardType.numer
only suggest the type of keyboard. For example, by default androud is use google keyboard. The keyborad will display with number only. But for another keyboard, like `microsoft keyboard ( I use this daily ). even we set the type, we can able to type non-digits