Flutter null safety conditional widget - flutter

Before Flutter introduced the null-safety feature, I was able to conditionally add Widgets within a list like so:
actions: <Widget>[
canCancel
? CupertinoDialogAction(
child: Text(cancelActionText),
onPressed: () {
Navigator.pop(context);
},
)
: null,
].where(notNull).toList()
notNull being a homemade filter that filters off the null objects...
Now with null-safety it's impossible because the list of Widgets strictly has to be non-null.
What would be a better approach?

Just use if inside the List:
<Widget>[
if (true) Widget(),
]
Example with your code:
actions: <Widget>[
if (canCancel)
CupertinoDialogAction(
child: Text(cancelActionText),
onPressed: () {
Navigator.pop(context);
},
),
]

Just replace your null with an empty, size zero, SizedBox.
SizedBox(width: 0, height: 0)
Or as suggested in comments:
SizedBox.shrink()

As YoBo suggested, using collection-if is the better approach here, but if for some reason you need to be able to store nulls in a List and filter them out later (or if you just prefer your existing style), you can:
Change your List type to allow nullable elements. That is, use <Widget?>[] instead of <Widget>[].
Use Iterable.whereType with a non-nullable type to filter out null values.
actions: <Widget?>[
canCancel
? CupertinoDialogAction(
child: Text(cancelActionText),
onPressed: () {
Navigator.pop(context);
},
)
: null,
].whereType<Widget>().toList();

if is not new as it was added in Dart 2.3 a few years ago. Even before null-safety, you were not allowed to return a null for a widget. You might not see a compile-time warning before NNBD, but it is a runtime error.
As I mentioned in this answer (although that answer isn't for your case), you can use if condition or even ternary operator like this:
Column(
children: [
if (condition) Text(''),
condition ? Text('') : Container(),
],
)

Related

Printing values of a String List in flutter without invoking the null error

I have this list
List? mcq;
Now, when after adding values in it, when I try to print the values on the app screen using the Text widget it gives a null error.
child: TextButton(
child: Text(mcq?[0]),
onPressed: () => {}),
It says type 'Null' is not a subtype of type 'String'.
If I remove the ? it says [] cannot be unconditionally invoked because the receiver can be null.
Then how do I print the values of a list using Text widget?
You need set a default value incase it get null, like this:
child: TextButton(
child: Text(mcq?[0]?? "some thing"),//<---- add this
onPressed: () => {}),

How do i modify the data of an existing variable in flutter?

I want to make an editable TextWidget in flutter but I don't really know how to go around it, I did some research, but still can't find a good solution.
Here's my sample code below.
I have a variable called
int qty = 1;
and so I called the variable in TextWidget
Column(
children: [
Text(
"${qty}",
style: TextStyle(),
)
],
),
I want to have these features that make user tab on the value to change it if they want, upon tap, a pop-up dialog will show to give the user the ability to change the existing value to whatever the user wants.
Please if anyone knows how, please help.
You will need a statfull widget to call setState and make the UI update with the new value stored in your qty variable. (I'am assuming that you are not using any state managment).
I wrote a possible solution for what you need.
Let look into some considerations:
Text will show whatever is in the qty as long we call setState after (or do it inside) we change the value of qty.
You need some widget to detect your tap. If you want to the text be 'clicable' then it should be wraped inside that widget.
The onTap/onPress call back of that widget should show a new widget. For this you can use the already made showDialog() and pass it a Dialog Widget. in here you will put your ui for that.
In some point of that UI you need to introduce the new value. So you can use a simple TextField that will save the introduced value, where you can assign it to qty, without forgetting to call setState! Note that it deal with strings, so you neet to do an int.parse() ou double.parse accordingly to you qty var type.
And I think that's it.
The could be other ways of doing it. This is a good and simple approach for your need.
I wrote a piece of code to help or somelse how is trying to do it:
InkWell(
// can be gesture detector, button, etc
onTap: () => showDialog(
context: context,
builder: (context) => Dialog(
child: Container(
color:
Colors.white60, // change it accordingly to you
height: 80, // change it accordingly to you
width: 200, // change it accordingly to you
child: Column(
children: [
const Text('Change your value here'),
TextField(
decoration:
InputDecoration(hintText: qty.toString()),
onChanged: (insertValue) => setState(() {
qty = int.parse(insertValue);
}),
// you can use other callBack function (like onComplete,
// onSaved), wich is more eficient than calling setState eveytime,
// but you have to do the needed adtaptions. Like onSave
// needs a key to call the save function. is easy just google it.
),
],
)),
)),
child: Text(
"${qty}",
),
),
What you are probably looking is a DropdownButton.
You would have something like this:
int qty = 1;
List<int> listOfValues = [1,2,3,4];
and then in your column you would have
DropdownButton<int>(
// This are the list of items that will appear in your dropdown menu.
// items is all the options you want your users to be able to select from,
// and it take a list of `DropdownMenuItem`. So instead of creating a `DropdownMenuItem`
// for each of the items in `listOfValues`, we iterate through it and return
// a `DropdownMenuItem`
items: listOfValues
.map((item) => DropdownMenuItem<int>(
value: item,
child: Text('$item'),
))
.toList(),
value: qty,
onChanged: (value) {
if (value != null) {
setState(() {
qty = value;
});
}
},
),
For more information on DropDownButton, check the following links:
https://api.flutter.dev/flutter/material/DropdownButton-class.html
https://www.youtube.com/watch?v=K8Y7sWZ7Q3s
Note: In a scenario where you want to increase the quantity of an item, like in a shopping cart, maybe having a button increment qty by 1 would be better.

Flutter- how to disable a Button untill the requireed fields are filled

I want to disable a button untill all the required fields are filled. I found similar questions here but all answers are based on making the onPressed property to null. But it does not disable the tap effect.
I want something like this. On disabled mode, clicking on the button won't even cause a Tap effect. Can someone help with a solution?
For a limited number of widgets, you can wrap them in a widget IgnorePointer: when its ignoring property is set to true, the sub-widget (actually, the entire subtree) is not clickable.
IgnorePointer(
ignoring: true, // or false
child: CustomButton(
onPressed: _login,
child: Text("Login"),
),
)
checkout this widget.set absorb to true when the required field is empty or is not validated.
https://api.flutter.dev/flutter/widgets/AbsorbPointer-class.html
In the textField add
onChanged : (val){ setastate((){});}
You mustbe already having a TextEditingController for the textfield. For example i shall name it phoneTextController.
Now in the button check the condition
phoneTextController.text.length > 10
For example
Inkwell(
onTap:(){
if(phoneTextController.text.length > 10){
SendOtp()
}
}
child: Container(
color: phoneTextController.text.length > 10 ? Colors.blue : Color.blue.withOpacity(0.5),
)
)
If you're using one of Flutter's built-in button Widgets, setting the onTap to null should automatically disable the button and its effect. Now all that remains is to conditionally do this during your build. Say, like the other answer, your TextEditingController is named phoneTextController:
ElevatedButton(
child: Text('Next'),
onTap: phoneTextController.text.length > 10 ? () => goToNextPage() : null,
),
Which will automatically enable the button and add the callback as soon as the condition is met (in this example, the input length is >10.

Create dynamic return depending on logic behind in dart?

I am a beginner at dart/flutter. Is it possible to create a function or ... like this in dart? Seriously, I don't know what it is called! Let me explain what I wanted. Let say my app have 2 account type "premium" & "free". I want to create a widget or callback function or whatever which can return Type depending on my app account type (this calculated in underlying):
- Use in widget
Container(
child: AccountType.when(
(premium) => PremiumWidget(),
(free) => FreeWidget(),
),
),
For checking account type is done in underlying.
- Use in function
IconButton(
onPressed: AccountType.when(
(premium) => () {},
(free) => () {}
),
icon: Icon(Icons.ac_unit),
),
How can I achieve that? Any help would be appreciated. Thank you
You can use enum and if/else for that. First create the AccountType enum:
enum AccountType {
premium,
free,
}
Usage in widget:
Container(
child: (accountType == AccountType.premium ?
PremiumWidget() :
FreeWidget())
);
Usage in function:
IconButton(
onPressed: () {
if (accountType == AccountType.premium) {
// do something for premium account
} else {
// do something for free account
}
},
icon: Icon(Icons.ac_unit),
);

How to display a column when a variable is true in Flutter?

I keep getting this error that res is not defined, so I am trying to figure out how to run the column code after res is defined like in js you can do {res && (code)}.
Column(
children: res['rooms'].map((r) => Card(
'name',
'${r['messages'][r['messages'].length - 1]['content']}',
'${r['_id']}'
)),
),
You can check if your res variable is equal to null.
This is the non nullable approach to your problem, but you should check to migrate your project to null safety to avoid all those null checks.
res == null
? Column(
children: res['rooms'].map((r) => Card(
'name',
'${r['messages'][r['messages'].length - 1]['content']}',
'${r['_id']}')),
)
: Container(),
It can be hard to read ternary expressions sometimes if you have a large widget within a expression. It also requires you to provide an "else" widget.
I approach this with the spread operator, with a single if statement
Column(
children: <Widget>[
if (res != null)
...res.map((r) => Card(
'name',
'${r['messages'][r['messages'].length - 1]['content']}',
'${r['_id']}')),
],
)
Simple use ternary login like this -
Container(
child: res != null
? Column(
children: []
)
: SizedBox() //Use SizedBox instead of Container for better efficiency
)
And update the UI again once "res" is populated.