How can I archive tristate for checkbox with nullsafety.... i have to pass null into that value but nullsafety has to perform null check on a variable, this is just contradicting .....
Putting null check as usual:
bool? parentvalue;
void update() {
parentvalue = null;
}
Checkbox(
....
value: parentvalue!,
onChanged: update(),
....
),
ERROR: Null check operator used on a null value
if I remove null check, code cannot compile at all
bool parentvalue;
void update() {
parentvalue = null;
}
Checkbox(
....
value: parentvalue,
onChanged: update(),
....
),
ERROR: A value of type 'Null' can't be assigned to a variable of type 'bool'.
=================================================================
UPDATE:
Checkbox(
tristate: true,
checkColor: Colors.white,
fillColor: MaterialStateProperty.resolveWith(getColor),
value: isChecked,
onChanged: (bool? value) {
setState(() {
isChecked = value!;
});
},
),
ERROR: Null check operator used on a null value
if i remove null check
onChanged: (bool value) {
setState(() {
isChecked = value;
});
},
ERROR: The argument type 'void Function(bool)' can't be assigned to the parameter type 'void Function(bool?)?'.
You need to add tristate: true in order to accept null. Also, the update() method needed to be changed because it provides nullable bool on callback.
Checkbox(
tristate: true,
value: parentvalue,
onChanged: update,
// onChanged:(value) { update(); },
),
And update method will be
void update(value) {
parentvalue = value;
setState(() {});
}
More about Checkbox on flutter.dev.
Here's an example with setting tristate to true:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Row(
children: [
MyCheckbox(),
],
),
),
);
}
}
class MyCheckbox extends StatefulWidget {
const MyCheckbox({Key? key}) : super(key: key);
#override
State<MyCheckbox> createState() => _MyCheckboxState();
}
class _MyCheckboxState extends State<MyCheckbox> {
bool? parentValue;
void update() {
setState(() {
parentValue = null;
});
}
#override
Widget build(BuildContext context) {
return Checkbox(
tristate: true,
value: parentValue,
onChanged: (_) {
update();
});
}
}
tristate property used for pass null value to checkbox as place of boolean
so if you want to pass null value to checkbox without showing any error then you have to set true value to the property of checkbox named tristate
like this
CheckBox(
....
tristate:true,
value:null,
...),
Let's discuss how Null safety works.
If you expecting a value(variable/object) that shouldn't be null then simply make that value null safety. In your case
bool? parentvalue .
& if you read Checkbox property
/// This property must not be null.
final bool? value;
/// If tristate is false (the default), [value] must not be null.
final bool tristate;
here value is null safety & you cannot assign null value on it.
so if you assign your parentvalue null this will be not accepted by, Checkbox value property. it only except Boolean(true/false).
void update() {
//Wrong
parentvalue = null;
}
So either you should make your parentvalue by default true or false as per requirements. or you can also use value: parentvalue??false/true to maintain null safety.
the value of Checkbox must be nut null
if you want to Checkbox be unChecked when value is null you can use this instead of null check
value: parentvalue??false
Related
Situation:
I have a checkbox in one place and i am sending the callback etc. up the widget tree to run a setState and run the function applyFilters().
The NeededChecked is also routed up to the checkbox-value.
Question:
What i am struggling to understand is why this works.
Specifically how the onPressed callback is able to set the value of the bool isNeededState to true/false?
Here is the code that is run. The only important part is the passing of the bool isNeededState to the neededCheked.
void neededFilterCalled(bool isNeededState) {
setState(() {
NeededChecked = isNeededState;
applyFilters();
});
}
And here is the checkbox widget:
Widget build(BuildContext context) {
return Checkbox(
value: isNeededChecked,
onChanged: neededFilterCalled,
);
}
Writing
onChanged: neededFilterCalled,
is shorthand for
onChanged: (value) => neededFilterCalled(value),
onChanged provide nullable bool, defined as
required void Function(bool?)? onChanged
You can accept null value and provide false on null case like
void neededFilterCalled(bool? isNeededState) {
setState(() {
isNeededChecked = isNeededState ?? false;
applyFilters();
});
}
return Checkbox(
value: isNeededChecked,
onChanged: neededFilterCalled,
);
Before I updated my dart version to support only non-null that code was working fine:
Radio(
value: ExcerciseType.weight,
groupValue: type,
onChanged: (ExcerciseType value) {
appState.exerciseType = value;
},
),
Now the compiler complaints:
"The argument type 'void Function(ExcerciseType)' can't be assigned to the parameter type 'void Function(ExcerciseType?)?'.",
This would fix it:
onChanged: (ExcerciseType? value) {
appState.exerciseType = value as ExcerciseType;
},
but that fix is not logical at all. With the as cast I just say value might be null at runtime and appState.exerciseType can accept null values but that is not true at all! appState.ExerciseType is of type ExcerciseType and not ExcerciseType?.
What do I not understand here?
onChanged: (ExcerciseType value){} means you will get non-nullable value. But
onChanged: (ExcerciseType? value){} means you will receive nullable ExcerciseType date.
The default onChanged: provide nullable data.
ValueChanged<T?>? onChanged
We can have nullable radio button, means it will actually return null after selecting one. And it is useful for some cases. This snippet show you can have null as return. Here, 1st radio button will return null.
int? value;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: List.generate(
5,
(index) => Radio<int?>(
value: index == 0 ? null : index,
groupValue: value,
onChanged: (int? v) {
print(v);
setState(() {
value = v;
});
},
),
)),
);
}
}
You can directly use ! if you're certain about non-nullable data.
Radio<ExcerciseType>(
onChanged: (ExcerciseType? value) {
appState.exerciseType = value!;
}
)
More about onChanged and null-safety
When I change the "Ulke" value from the AsyncSelectInputForm, I call the notifyListeners() method so that the "Il" value is null.
When I do this, the value I entered in the "Adres Başlığı" TextInputForm returns to its initial value.
My widget:
#override
Widget build(BuildContext context) {
var cariAdres = Provider.of<CariAdresProvider>(context);
return Column(
children: [
TextInputForm(
initialValue: cariAdres.cariAdres?.adresBasligi,
label: "Adres Başlığı",
onChanged: (value) {
cariAdres.cariAdresTemp =
cariAdres.cariAdresTemp?.copyWith(
adresBasligi: value,
);
},
),
//todo ulke select
AsyncSelectInputForm(
pageTitle: "Ülke Seç",
label: "Ülke",
initialLabel: cariAdres.cariAdresTemp?.ulkeIdStr,
labelSetter: (item) {
return item.ulkeStr;
},
onChanged: (value, item, context) {
cariAdres.cariAdresTemp =
cariAdres.cariAdresTemp?.copyWith(
ulkeId: value,
ulkeIdStr: item.ulkeStr,
ilId: null,
ilIdStr: null,
);
cariAdres.notifyListeners();
},
fetchPage: //...,
),
//todo il select
AsyncSelectInputForm(
initialValue: cariAdres.cariAdresTemp?.ilId,
//... same code
)
//....
It can be related a lot of possibilities, so we can't be sure which one is correct. But you can try to add some debugPrint in your build method in this way, you can expand your understanding for the situation.
Also, it can be about some logic in your change notifier provider or it can be about your widget tree-state or it can be about your sub widgets.
My problem was that I had set the initialValue of TextInputForm to value cariAdres.cariAdres?.adresBasligi and valued cariAdres.cariAdresTemp?.ulkeIdStr to AsyncSelectInputForm's initialLabel.
I was able to fix this problem by replacing the cariAdres.cariAdres?.adresBasligi value with the cariAdres.cariAdresTemp?.adresBasligi value. :)
I'm developing a mobile app in Flutter and have encountered a problem while trying to pass a function as a parameter to a widget.
To be more precise:
class Test extends StatefulWidget {
final Function(bool) onChanged;
const Test({Key key, this.onChanged}) : super(key: key);
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
bool switchValue = false;
#override
Widget build(BuildContext context) {
return Container(
child: Switch(
value: switchValue,
onChanged: (bool value) {
setState(() => switchValue = value);
widget.onChanged(value);
}));
}
}
It throws NoSuchMethodError: "The method 'call' was called on null" when the widget is used without defining the onChanged function.
How to define a default function for the onChanged parameter? The parameter should be optional.
I have tried with:
() {} - A value of type 'Null Function( )' can't be assigned to a variable of type 'dynamic Function(bool)'.
(bool) {} - The default value of an optional parameter must be constant.
Solutions without using default value are:
to check if onChange parameter is not null before calling it, or
to define it every time when the widget is used - onChanged: (bool val) {}
Any suggestions would be appreciated.
You can define a default function like this.
void emptyFunction(bool value) {
if (value) {
// do something
} else {
// do something
}
}
const Test({Key key, this.onChanged = emptyFunction}) : super(key: key);
you can check a sample code showing this in action on dartpad. https://dartpad.dev/30fc0fdc02bec673779eebc733753c05
I want to achieve different values behind the scene, for example, if the user selects "United State of America" behind the scene I want a value "USA" only. How can I achieve this?
Here is my button:
DropdownButton<String>(
isExpanded: true,
underline: SizedBox(),
icon: SvgPicture.asset("assets/icons/dropdown.svg"),
value: dropdownValue,
items: [
'Nepal',
'India',
'United States',
'Denmark',
'UK',
'World Wide'
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (newValue) {
setState(() {
dropdownValue = newValue;
});
},
),
What you can do is create a CountryOption class with a key('USA' in your example) and a fullName ('United States' in your example).
You then create a list of dropdown items of CountryOption instead of String, so you can store the currently selected CountryOption and you have both the key and fullName properties available for later use.
I would also recommend loading your list of items only once, not on each rebuild.
import 'package:flutter/material.dart';
class CountriesButton extends StatefulWidget {
const CountriesButton({Key key}) : super(key: key);
#override
_CountriesButtonState createState() => _CountriesButtonState();
}
class _CountriesButtonState extends State<CountriesButton> {
List<DropdownMenuItem<CountryOption>> _countryItems;
CountryOption _selectedCountry;
#override
void initState() {
// Get all countries
List<CountryOption> countries = CountryOption.allCountries;
// Initialise your items only once
_countryItems = countries.map<DropdownMenuItem<CountryOption>>(
(CountryOption countryOption) {
return DropdownMenuItem<CountryOption>(
value: countryOption,
child: Text(countryOption.fullName),
);
},
).toList();
// Initialiste your dropdown with the first country in the list
// (might be different in your specific scenario)
_selectedCountry = countries[0];
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: DropdownButton<CountryOption>(
isExpanded: true,
underline: SizedBox(),
icon: SvgPicture.asset("assets/icons/dropdown.svg"),
value: _selectedCountry,
items: _countryItems,
onChanged: (newValue) {
setState(() {
_selectedCountry = newValue;
});
},
),
);
}
}
class CountryOption {
final String key;
final String fullName;
CountryOption(this.key, this.fullName);
static List<CountryOption> get allCountries => [
CountryOption('nepal', 'Nepal'),
CountryOption('india', 'India'),
CountryOption('USA', 'United States'),
CountryOption('denmark', 'Denmark'),
CountryOption('uk', 'UK'),
CountryOption('world', 'World Wide'),
];
}
Let me know if something is not clear or if you have any questions.
Most simple method is to create a switch case and pass in the dropdownvalue.
Then define your cases and default option. You can make this with a function inside the class or in separate class.