Trying to check userStatus and currentUser while comparing it with a value but it is not working and on the other hand if I put not equal to then it works.
I want to compare the value like mentioned below:-
body: currentUser == null ?
Center(child: Splash())
:currentUser.userStatus == 'isBlocked'
? BlockUser():
Center(
child: Container(),
And on the other hand if I put != sign then it works:-
body: currentUser != null ?
Center(child: Splash())
:currentUser.userStatus != 'isBlocked'
? BlockUser():
Center(
child: Container(),
But here I want to compare the values which are stored in database and then show the desired result. Pls help.
As I hinted about in the comment to your question. I would suggest to ditch the ternary operator and write a method instead.
Ternary operators are best suited for small and very simple logic.
The code will be more readable and simple mistakes wouldn't happen as easily. Instead do something like this:
body: myAwesomeUserChecker(),
...
With the method:
Widget myAwesomeUserChecker() {
if (currentUser == null) {
return Center(child: Splash());
}
if (currentUser.userStatus == 'isBlocked') {
return BlockUser();
}
return Center(child: Container());
}
Ps. This matches the first option that you wrote, but which you said didn't do what you wanted... The second option you wrote somehow did what you expected, but logically that alternative seemed flawed. So, if what I wrote here doesn't give you your expected outcome, then I would assume the fault might be somewhere else, or you'd have to provide more information so that the case can be better understood.
Please define your expected behavior more precisely. If we better understand your desired outcome, we can help better.
I tried to unify the indentation and punctuation of your code and added some comments. Does this accurately represent the behavior you intended and it is still not behaving as the comments below indicate?
body: currentUser == null ?
// If currentUser == null, Splash() will be centered
// userStatus will not even be checked
Center(child: Splash()) :
// Else (currentUser != null), userStatus will be checked
currentUser.userStatus == 'isBlocked' ?
// If currentUser != null and userStatus == 'isBlocked' then BlockUser()
BlockUser() :
// Else (currentUser != and userStatus != 'isBlocked') center Container()
Center(child: Container()
Well, before making this logic, try to print the value of both currentUser and the value currentUser.userStatus to know what the currentUser has and what the currentUser.userStatus returns. After that, you may make a logic using simple if-else statements. Once you get success in that, then convert it to ternary condition.
Related
I have been using Flutter BLoC for some time and I'm quite content with it. However I am still wondering what is the best practice to adopt for the "default case" in BlocBuilder.
Imagine I have the code below which displays various based on the state of a Request (say to an API) :
BlocBuilder<RequestBloc, RequestState>(
builder: (context, state) {
if (state is RequestOk) {
const Text("Name = ${state.firstname}");
const Text("Name = ${state.lastname}");
const Text("Name = ${state.age}");
}
if (state is RequestLoading)
{
const CircularProgressIndicator(color: Colors.orange);
}
else {
return Container(); // any better way than this ?
}
},
);
The RequestOk and RequestLoading are the only two states I wish to display something specific for. Therefore, I need to implement the else as well because the BlocBuilder needs to return a Widget.
Currently, I return an empty Container and even tho it seems to be an "accepted" solution by many, I cannot help myself but think it is just a trick. Does anyone know if there is a more "correct" solution to deal with any other states ?
Thank you all very much in advance.
I've been wondering what's the best practice for conditional widget trees between using ternaries and if-else-blocks.
Following guides online, I feel people use mostly ternaries but I find them quite unreadable when they exceed a single line :
So I tend to create a fonction and make an if-else-block when my ternary are too long :
floatingActionButton: selectedLicences.isEmpty //
? Container()
: LicencesWidget(selectedLicences: selectedLicences)
Widget _buildFAB(List<X> licences) {
if (licences.isEmpty) {
return Container();
} else {
return LicencesWidget(selectedLicences: licences);
}
}
What's the best practice?
floatingActionButton: selectedLicences.isEmpty ? Container()
: LicencesWidget(selectedLicences: selectedLicences)`
This is the best practice for sort conditions as per my knowledge.
Using ternary is Conditional expressions and if-else are statements.
Sometimes on widget tree if-else won't work, because it seeks for expression.
Like you cant use conditional if-else statements on MaterialApp's home and scaffold's body
/// theses wont work
MaterialApp(
home: if(1<4) Text("") else Text('')
home: Scaffold(body: if(1<4) Text("") else Text(''),)
or
Text(1 < 4 ? Text("") : Text(''))
But If you use ternary expression here it will work
body: 1 < 4 ? Text("") : Text('')
Inside a Column widget or the widget that takes children, you can use conditional if-else statement.
You can check Benefits of ternary operator vs. if statement
In summery: Not all the time you can use if-else statement inside a widget, you will need to provide expression in that case. I prefer using ternary expression over if-else statements.
Also, you can check about inline function, [It is recommended not to do heavy operation]
home: () {
if (1 < 4) {
return Text("");
} else {
return Text('');
}
}(),
About your snippet answer I will pass null instead of empty container.
floatingActionButton: selectedLicences.isEmpty
? null
: LicencesWidget(selectedLicences: licences)
I have upgraded my project to null-safety and it's a bit confusing, because if I wrap into a
if(someObject.field != null) {
doSomething(someObject.field); // error, can't assign String? to String
}
a method call that requires a not-nullable property and my variable I am trying to pass is nullable, then I get a type error that I cannot assign a nullable value to a not-nullable type.
But when I do
String? someObjectField = someObject!.field;
if(someObjectField != null) {
doSometing(someObjectField); // Can assign
}
it works as expected.
doSomething = (String foo) {}
For example:
class Person {
final String name;
Person(this.name);
}
Function test = () {
Map<String, String?> pers = {
'name': 'John',
};
if(pers['name'] != null) {
Person(pers['name']); // Error, even if it will be never executed
Person(pers['name'] as String); // no error
Person(pers['name']!); // no error
}
};
And if I do something like:
if (widget.event != null && widget.event.featuredImage != null)
Then it complains in the second statement that the receiver (widget.event) can be null and I need to use !, but the second statemen should never execute and it should never cause a runtime exception.
So I need to modify it to:
if (widget.event != null && widget.event!.featuredImage != null)
But then when I try to use a nested widget inside Flutter, then even if I use the if as wrapper I still need to add ! everywhere
Stack(
children: [
// Add Container with image only when event and futured image are not null
if (widget.event != null && widget.event!.featuredImage != null) ...[
Container(
height: 250,
decoration: BoxDecoration(
color: Colors.transparent,
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(widget.event!.featuredImage!), // here i Need to use ! to satisfy the compiler
),
),
),
],
],
)
Alternatively I can extract the variable into another one String? image = widget.event!.featuredImage then modify the if statement to if(image != null) and use the widget like NetworkImage(image) which works as expected.
Compared to TypeScript which detects if I checked for null in a condition above, this makes no sense to me.
Longstory short, even if I check for null value to render/not-render a component, I still need to use !.
Is there something obvious that I am missing?
Thank you in advance
Since you compared it to TypeScript, yes, you are missing something.
Typescript is a mess that works in a very limited environment and "works" is grossly overstated. For example you could write a method in typescript that takes a string and then at runtime find out it's not actually a string, surprise, it's a completely different type. The joys of JavaScript. Calling TypeScript "type safe" is correct compared to JS and ridiculous compared to actually compiled languages.
So you are missing the fact that the Dart compiler can guarantee that something is not null once you checked it. To do that, it needs additional constraints. For example, you could have getters that do not return the same value every time you call them. Your call for example could easily return different values between the first and second call, depending on the code of the getter. Or you could use inheritance and polymorphism to build some even more problematic constructs. See here for an interesting example. So you need to have a local variable, that is guaranteed to have the same value, unless explicitely changed.
Your example of if (widget.event != null && widget.event.featuredImage != null) could easily be modified to:
final image = widget?.event?.featuredImage;
Stack(
children: [
// Add Container with image only when event and futured image are not null
if (image != null) ...[
Container(
height: 250,
decoration: BoxDecoration(
color: Colors.transparent,
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(image),
),
),
),
],
],
)
Yes, you have to implement some tiny bit of logic, you cannot just slap ? and ! on your code and it runs as before. But once you understood what that little change in logic is, it is pretty easy.
I have simple code as follows:
class TestPage extends StatelessWidget {
static String myString = 'abc';
static double myDouble;
#override
Widget build(BuildContext context) {
try {
myDouble = double.parse(myString) + 5;
} catch (e) {
print(e);
}
return Scaffold(
body: Center(
child: Text(
'$myDouble' ?? 'Ok',
style: TextStyle(fontSize: 50),
),
),
);
}
}
Since myDouble is going to return null, I expect the Text widget to take in 'Ok' and rendre the same. However, I still see null shown on the screen.
Any help as to why this is not working please. Thank you.
Additionally, I do not understand why I cannot use try catch block before build method as it gives error.
Disclaimer: This question is regarding Dart before the null-safety. With null-safety on, this scenario would not happen.
In your case, even though myDouble is null, when you do
'$variable'
You are creating a new string which isn`t null (which is denoted by the " or ' signs).
So the 'ok' after the null-aware operator would never be called anyway.
Also, using ${var} inside a string is called String Interpolation, which as the documentation states:
To get the string corresponding to an object, Dart calls the object’s toString() method.
And, the toString() method on a null object returns simply null, which results in your text displaying null.
There are many ways for the desired result, you could do, as an example:
(myDouble == null) ? 'ok' : '$myDouble' ,
Just as a reminder, Dart now supports null safety, which you should be using in new codes (and migrating old ones). With null safety, you probably would have notice this error before.
Text('$myDouble') converts the null to string null
'$myDouble' is now 'null'(string)
to manage null String variables you can do this either of the ways
Text(myDouble == null ? 'Ok' : myDouble.toString);
Why does the first work, but the second does not? The issue with the first is that null throws assertion exception since the widget cannot be null. what is the best practice in this scenario?
Row(
children: listOfWidgets != null ? listOfWidgets : null
)
Row(
children: if (listOfWidgets != null) listOfWidgets;
)
Current workaround:
void List<Widget> _rowWidgets() {
if(listOfWidgets != null) return listOfWidgets;
}
Row(
children: _rowWidgets();
)
Each of the three options listed has its own problem, so I'll address them individually.
Issue:
Row(
children: listOfWidgets != null ? listOfWidgets : null,
)
Solution:
Row(
children: listOfWidgets != null ? listOfWidgets : [],
)
Explanation
This is fine except for if listofWidgets is null. That would cause the ternary operator to return null, and Row doesn't allow its children property to be null. You need to give it a non-null default value like [] instead.
Issue:
Row(
children: if (listOfWidgets != null) listOfWidgets;
)
Solution:
Row(
children: [ if (listOfWidgets != null) ...listOfWidgets ],
)
Explanation:
This syntax is wrong in Dart because you can't have a nested if statement like this. if statements have to either exist on their own live of code or (since Dart 2.3) nested within a list/map literal. If you do the nested-in-list-literal route, you will also need the spread operator ... on the listOfWidgets to expand it into the other list. Otherwise, you will end up trying to put a list within a list, which Row will not allow you to do.
Issue:
void List<Widget> _rowWidgets() {
if (listOfWidgets != null) return listOfWidgets;
}
Row(
children: _rowWidgets();
)
Solution:
List<Widget> _rowWidgets() {
if (listOfWidgets != null) return listOfWidgets;
return [];
}
Row(
children: _rowWidgets(),
)
Ignoring the more glaring syntax errors (semicolon where a comma is expected, dual return types), this one is similar to the ternary issue, albeit a bit more subtly. In Dart, every method that doesn't have void as a return type has to return something. If you don't explicitly return something, the method will return null by default. This is what happens in your _rowWidgets method - you don't state what would be returned if listOfWidgets is null, so Dart will return null for you in that case. And just like the ternary issue, Row will not allow children to be null.
Overall Suggestion:
Row(
children: listOfWidgets ?? [],
)
Explanation:
Ultimately, all you are concerned with is making sure the Row receives a non-null value if listOfWidgets happens to be null. Dart has the perfect operator for this: the null-coalescing operator ??. What it does is it returns the value on the left unless that value is null, in which case it returns the value on the right.
To illustrate it more clearly, the following lines of code perform identically:
// If option
if (listOfWidgets != null) return listOfWidgets;
else return [];
// Ternary option
return listOfWidgets != null ? listOfWidgets : [];
// Null-coalescing option
return listOfWidgets ?? [];
The first case, the ternary has the form statement? a : b, and always returns or a or b. Thats the trick, the ternary always has a return statement even if we cant see it. The if doesnt has that statement by default, so you need to explicitly declare the return if you want one.