Flutter textfield behaves strange (updating with the wrong value) - flutter

I ran into a weird behavior related to the TextFormField, so I maintained a list of Objects in the parent widget, I'm using the following code to render a list of child widgets
children: <Widget>[
...childProfiles
.map((e) => ChildProfileCard(
childProfile: e,
removeChildProfile: removeChildProfile))
.toList(),
And the ChildProfileCard includes a TextFormField, the code is like the following
TextFormField(
decoration: const InputDecoration(
contentPadding: EdgeInsets.only(top: 0),
hintText: "Enter child's name",
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Name is required';
}
return null;
},
),
There is a "remove" function that simply removes one of the items from the list like the following
setState(() {
childProfiles = childProfiles
.where((childProfile) => childProfile.id != childProfileToRemove.id)
.toList();
});
When there are more than two items (two child widgets), I input some texts in the TextFormField in the first child widgets, then I remove the first item, the text will always automatically apply to the second child widget, what did I do wrong? I can confirm the list is correctly updated, but the text behaves strangely.
Before deleting, you can see we have different texts for different widgets
After deleting, the first widget's text is wrongfully copied over to the next widget, you can see the uuid is the second widget's.

You should Use any unique key while building ChildProfileCard
ChildProfileCard(key: Key(<ANY UNIQUE VALUE>),)
Example
ChildProfileCard(key: Key(e.id),)

Related

is there any way to control dropdown items based on another dropdown's value? [flutter]

I have stateless class contains form with 4 tabs and one of those tabs contains 3 dropdowns to select address on for country and based on value selected I want the second dropdown (which is for cities) to view its items.
all items for dropdowns comes from local sqlite database.
the problem is the second dropdown dose not view its items but when I use debugger I found the list comes from database successfully but the update of list value on stateless class not happen.
any way to solve this??
DropdownButtonFormField<Region>(
decoration: InputDecoration(
isDense: true,
floatingLabelBehavior:
FloatingLabelBehavior.auto,
labelStyle: TextStyle(fontSize: 22),
contentPadding:
EdgeInsets.symmetric(vertical: 9),
),
value: regionList.isEmpty
? region
: helRegion.getReg(
user.regionAdresse, regionList),
//icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
onChanged: (selectedRegion) {
onRegionSelected(context, user,
region, province, provinceList, selectedRegion);
},
items: regionList
.map((project) => DropdownMenuItem(
child: Text(project.Name),
value: project,
))
here is onRegionSelected function:
onRegionSelected(BuildContext context, User user, Region region, Province province, List<Province> provinceList, Region selectedRegion) async {try { showLoadingDialog(context);
final _provinceList = await getProvinceList(selectedRegion.id);
region = selectedRegion;
user.regionAdresse = selectedRegion.id;
province = null;
//provinceList.clear();
provinceList = _provinceList;
Navigator.pop(context);} catch (e) {
//TODO: handle error
rethrow; }}
First of all, you should use a stateful class so that when you select the first dropdown, the information that will appear in the second one is updated, and so on.
If your idea is to keep the format stateless, you could also save the responses from the DB in a stream and wrap the dropdowns in streamBuiler, so they could be updated when the new data pointed to by each dropdown is updated.
I'm leaving this as an answer since I don't have the reputation to add it as a comment.

How make default selection in FormBuilderDropdown list in flutter?

I am having a multiple FormBuilder dropdown in my flutter app. all of them are dependent dropdown.
i want to implement something like this:
the first of the main parent dropdown value should be auto fill and hence all other are filled or selected depending on the parents selection.
I can see the text filled in all the dropdown but as it is the dependent it is disabled for first dropdown to be clicked once.
How to fix this issue?
in the below screenshot we can see that the data is filled or been selected in every dropdown but second dropdown is still been disabled. it only gets enabled after clicking or selecting the value in first dropdown. enter image description here
Here is my code for first dropdown
padding: const EdgeInsets.all(12.0),
child: FormBuilderDropdown(
name: 'City',
decoration: InputDecoration(
labelText: 'City',
),
allowClear: true,
hint: Text(widget.callLocationModel.CityName),
initialValue: citySelected,
validator: FormBuilderValidators.compose(
[FormBuilderValidators.required(context)]),
items: cityList
.map((citySelected) => DropdownMenuItem(
value: cityChoosen,
child:new Text('$citySelected'),
))
.toList(),
onChanged: (String value){
setState(() {
citySelected= value;
//this fuction calls my second dropdown depending on first dropdown value selection
fetchSite(citySelected);
});
},
),
),
I assume all this layout is done in a stageful widget, which has a variable in it’s state for each selected value (when it’s selected). You may override initState member function and set the initial values there, so when the widget is built for the first time it will have those values preselected.

How to write data in firebase cloud firestore with data type using flutter

When saving data to the Firebase database I want every data to be saved according to its data type. But my all data is stored in String. How can I do it in flutter... like Amount will be int, Through will be String
[
here is my function
sandDataToDB() async {
CollectionReference _collectionReferance =
FirebaseFirestore.instance.collection("Use-of-fund");
return _collectionReferance
.doc()
.set({
"Details of Sector": _DetailsofSectorController.text,
"Through": _ThroughController.text,
"Amount": _AmountController.text,
"Date": _DateController.text,
})
.then((value) => dialog())
.catchError((error) => Fluttertoast.showToast(msg: "something wrong"));
}
here is all code
customAdminTextField(
"Details of Sector", _DetailsofSectorController),
customAdminTextField("Through", _ThroughController),
customAdminTextField("Amount", _AmountController),
customAdminTextField("Date", _DateController),
const SizedBox(
height: 10,
),
customSubmitButton("Submit ", () {
sandDataToDB();
})
You have to parse your data. An example for an integer:
...
"Amount": int.parse(_AmountController.text),
...
Here you can find the supported data types.
According to your question, you might be using the controller in TextFormField, you can get the value of the TextFormField.
This widget covers a TextField widget in a FormField for convenience.
It is not necessary to have a Form ancestor. The Form merely simplifies the process of saving, resetting, or validating numerous fields at the same time. To use without a Form, supply a GlobalKey to the constructor and save or reset the form field with GlobalKey.currentState.
TextEditingController.text defines the initialValue when a controller is defined. A controller should be given if this FormField is part of a scrolling container that generates its children lazily, such as a ListView or a CustomScrollView.
A stateful widget ancestor of the scrolling container should handle the controller's lifetime.
Example code:
TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: 'What do people call you?',
labelText: 'Name *',
),
onSaved: (String? value) {
// This optional block of code can be used to run
// code when the user saves the form.
},
validator: (String? value) {
return (value != null && value.contains('#')) ? 'Do not use the # char.' : null;
},
)
You can also use the onSubmitted parameter in TextFormField. Like: onSubmitted: (String value)
Additionally, you can use something like this in the following code:
_formKey.currentState.save(); calls the onSaved() on each textFormField item, which gives all the fields a value and allows you to utilize them as needed.

Flutter Form controller throws "The method 'validate' was called on null"

The method 'validate' was called on null.
Receiver: null
Tried calling: validate()
I don't understand this. I thought maybe the problem was the Form isn't the root element of the class, it's not return Form(child: Column(children: [... So I tried making the Form Widget the root, it stopped the error, but didn't activate the TextFormField validator or save, it just said 'everything fine, move along'.
It's just one field I presently wish to validate. I've looked up other such queries, both the Form widget & the TextFormField have keys, so I'm stuck.
I declare the form key with final _formKeyForDeposit = GlobalKey<FormState>();
And here is the un-cooperative form:
Form(key: _formKeyForDeposit, child:
TextFormField(
controller: _controllerDefaultDeposit,
key: Key('defaultLoanDeposit'),
decoration: InputDecoration(
//icon: Icon(Icons.location_city),
labelText: 'Per item deposit',
hintText: 'Whole numbers',
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () {
_controllerDefaultDeposit.clear();
},
),
),
keyboardType: TextInputType.numberWithOptions(decimal: false, signed: false),
onSaved: (String amountStr) {
print("saving deposit");
user.defaultItemDeposit = int.parse(amountStr.trim());
},
validator: (String value) {
print(LOG + "validator called");
if(int.tryParse(value.trim()) == null) {
inputCompletionAlert += "But your default item deposit is not a number, please correct.\n";
return 'Not a £-- whole number monetary amount';
}
if(value == "" || value == "0") {
print(LOG + 'deposit validator called, should launch Dialog from here');
inputCompletionAlert += "Would you like to set a default deposit?";
return "Would you like to set a deposit?";
}
return null;
},
),
),
Have you tried building a custom validator function and then directly calling it from the validator property.
For example :-
Validator (String value) {
print(LOG + "validator called");
if(int.tryParse(value.trim()) == null) {
inputCompletionAlert += "But your default item deposit is not a number, please correct.\n";
return 'Not a £-- whole number monetary amount';
}
}
This was an incomplete question, this array of ExpansionBoxes messes up the validator:
ExpansionPanelList.radio(initialOpenPanelValue: 2,
children: [
bicyclePanel,
carPanel,
floodPanel,
diyPanel,
surplusPanel,
gardeningPanel,
ballSportsPanel,
snowSportsPanel,
waterSportsPanel,
campingPanel,
backpackingPanel,
circusPanel,
]),
I presume that when _formKeyForDeposit.currentState.validate() is called it heads down into the ExpansionPanelList and can't escape to trigger the validator of TextFormFields above it.
Since I only have 1 TextFormField outwidth the ExpansionPanelList, I've used _controllerDefaultDeposit.text to get the Deposit FormField value and manually validate it. It's a hacky solution, but it'll do for now.

Is there a way to always display the hint in a DropDownButton?

For example, in a TextField it is possible to set an InputDecoration with a label text, that shows in the center of the TextField when there is no user input, and then shows above the user's input text afterward.
With a DropDownButton I can only seem to get the hint text to display before the user makes a selection, and then it disappears and only displays the user's selection. Is there a way to mimic the TextField's behavior?
Thanks!
You can achieve that using DropDownButtonFormField widget instead of DropDownButton. DropDownButtonFormField has decoration property which lets you use labelText which goes over the top of the field after selecting an item from the list. Sample working code below:
return DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'select option'
),
value: selected,
items: ["A", "B", "C"]
.map((label) => DropdownMenuItem(
child: Text(label),
value: label,
))
.toList(),
onChanged: (value) {
setState(() => selected = value);
},
);
Output:
Hope this answers your question.