Why my method fails with the "boolean expression must not be null" exception, but SharedPreferences.getBool() don't? - flutter

I'm new to Dart and I'm wondering why I can't return null instead of a bool value in a method like this:
bool getFoo(SharedPreferences sp) {
return sp.getBool('foo');
}
But the SharedPreferences.getBool() method can return null:
bool getBool(String key) => _preferenceCache[key];
Both methods have the same return value type-hint. But the Dart's standard lib method can return null. When I call getFoo(prefs) I'm getting the exception Failed assertion: boolean expression must not be null in my method, not in the Dart's std lib method. Why? What's the difference? Why two methods with the same type-hint definition behave differently?

There is no difference in how the two functions treat the return of null.
Currently, all Dart functions with a bool return type can return null because all Dart types are nullable.
That doesn't mean that you can use null as a boolean, just as you can't call string methods on a null value with static type String. The null value represents the absence of an actual value, so if you end up using null in a condition (if, while, ?:) then you will get the error that you are now seeing.
Dart does not have coercion between different types, and you cannot treat a non-boolean value as a boolean, not even null. Obviously the language could have chosen to do so, say treat all non-true values as false (like it did in Dart 1, before the stricter type system of Dart 2), but it's actually fairly arbitrary whether null should mean true or false, so instead null means the absence of a boolean value, and therefore not being able to choose a branch of an if statement. This is consistent with all other places where null can flow, but its an error if it does and you try to use the value.
When Dart gets non-nullable types, then the conditions will be required to be (statically typed as) non-nullable booleans. This ensures that a null value cannot accidentally flow to a position where it is attempted used.
If you know that your result might be null, you can always substitute a default value explicitly: if (maybeBool ?? false) ...
Again, with non-nullable types, you will have to do that, if you haven't handled the null value earlier.

The issue was in the if statement. It doesn't accept nulls and doesn't cast null to boolean false. It requires a boolean value. I came to Dart from PHP and JavaScript so this moment was very unobvious for me. And I didn't read documentation carefully: https://dart.dev/guides/language/language-tour#if-and-else.
I actually can't see any profits from this strictness like in strictly typed languages while I can return the null result for a scalar type. https://github.com/dart-lang/sdk/issues/22 - this is the very early issue that Dart lang developers still implementing, but promise to finish it soon.

Related

The method 'compareTo' can't be unconditionally invoked because the receiver can be 'null'

How to fix this? Error Message: The method 'compareTo' can't be unconditionally invoked because the receiver can be 'null'.
Try making the call conditional (using '?.') or adding a null check to the target ('!').
Second Error: The argument type 'Object?' can't be assigned to the parameter type 'Object'.
How to fixed it?
It looks like you lack basic understanding about null safety features.
String? basically means that it's a String that can be null. This can be done with any type. A question mark ? at the end of the type name makes it nullable, and if it isn't there it can't be null.
The language is designed in such a way that you can't assign any objects that are nullable to variables that aren't or use methods of objects that might be null.
There are several ways to handle this.
1) don't make them nullable in the first place.
In your example, whatever the object is, it must have some field declared as String? name. Just remove the ? if possible. There's a good chance it will make other errors pop up, but it should be solvable, at least in the case where you don't allow it to be null. If for whatever reason you need it to be nullable this is not a solution.
2) tell the compiler that you are sure it's not null in this situation
This is done by adding ! at the end of the variable. In your example that would be:
b.name!.compareTo(a.name!)
This is probably the easiest solution but the program will throw errors at runtime if they do happen to be null
3) provide a fallback value using ??
?? basically is an operator that returns the left side if it's not null and otherwise the right side. You can use this to your advantage to provide fallback values. In your example you could do:
(b.name ?? '').compareTo(a.name ?? '')
This way it takes empty string in case the name is missing. This would be a safer option compared to option 2.
4) conditional calling using ?.
Maybe not applicable in your situation but good to know anyway. Let's say some class A has the method getName and you have an object a of type A? and this code:
String? b = a.getName();
this is not allowed because a can be null. But you can write this:
String? b = a?.getName();
This basically mean that it executes getName() and assigns it to b when a is not null or just assigns null otherwise, which is possible because b is allowed to be null here. Therefore, this is NOT possible
String b = a?.getName();
Now b is defined as not being nullable and since the assignment possibly can provide null you are not allowed to.

Dart null variable creation

I just started learning Dart. There is some problem when I am creating a null variable. When I type
String someVar;
it throws an error but when i type
dynamic someVar;
it doesn't. I tried doing alternative methods mentioned in Dart's doc but even those methods do not seem to work until i have a dynamic var type. Can anyone tell me what is it?
Dart has a feature called null safety, so when you define something which might have a null value, you have to use ?.
In your code above, try something like String? somevar;
If you are going to initialize the variable later, then define late String somevar;
If you have null safety on String someVar; will give you an error because it cannot be null.
If u want a variable to be a nullable string, use String? someVar.
Try avoiding the use of dynamic because it will allow for the type to not be fixed. This can potentially cause the accidental assigning of a value with a type you are not expecting.

I have few questions relating to the null safety feature which is offered by dart or flutter

I have few questions with regards to the null safety feature offered by Flutter/Dart.
Is it mandatory to make use of the null safety feature for flutter projects?
If I don't make use of the null safety feature, will I still be able to make use of all the existing and latest flutter widgets, with all the latest functionality they have got to offer? What I mean to say is, is there any specific functionality that is only available when the null safety feature is enabled.
What would be the major disadvantages, I could encounter, apart from the null check, if I don't make use of the null safety feature for the flutter projects?
Even if the code is correct, I find that the null safety feature would keep throwing the null check operator error, which is very annoying.
Could anyone please help with answering my queries, as I find this null safety to be very confusing and annoying. I did go through few videos but still, I'm not convinced.
Thank you so much for your help in advance.
Null safety can be overridden
Yes, you should still be able to make use of the new flutter widgets.
If you use don't null-safety, it will be slower
This is more like a statement than a question
Understanding Null Safety.
Is it mandatory to make use of the null safety feature for flutter projects?
No, you can still use the previous SDK constraints to not comply with NULL safety rules
environment:
sdk: ">=2.11.0 <3.0.0"
If I don't make use of the null safety feature, will I still be able to make use of all the existing and latest flutter widgets, with all the latest functionality they have got to offer? What I mean to say is, is there any specific functionality that is only available when the null safety feature is enabled.
Yes provided you set the environment as mentioned above or you can also specify at the top of your Dart file to disable null checks for that file.
// #dart=2.9
What would be the major disadvantages, I could encounter, apart from the null check, if I don't make use of the null safety feature for the flutter projects?
Note that eliminating null is not a goal. There’s nothing wrong with null. On the contrary, it’s really useful to be able to represent the absence of a value. Sometimes indicating absence is really important. It is not null that is bad, it is having null go where you don’t expect it that causes problems. So to avoid such crashes Dart migrated to Sound NULL Safety.
Even if the code is correct, I find that the null safety feature would keep throwing the null check operator error, which is very annoying.
You can definitely make some types NULL with the handy ? null-aware operator. For eg, you can make your String as String? provided indicating absence is really important and it won't cause problems in the future program flow.
Adding ! (null assertion operator )after any expression is equivalent to saying that "Hey Dart ! trust me that this value will never be null".
Initially, it might trouble you a lot. But remember, it's worthwhile migrating your apps to Sound Null Safety.
Further Reading -
https://dart.dev/null-safety
Why NULL Safety ?
example:
class Animal {
Dog dog;
}
class Dog{
String name;
}
Operator "!":
If you allow it to be null and allow it to throw an exception.
main(){
Animal animal = Animal();
print(animal.dog!.name); // throw exception dog is null
}
Operator "?"
If you want it will do nothing if the current parameter is null
main(){
Animal animal = Animal();
print(animal.dog?.name); // output emplty
}
You can give a replacement value of the parameter if the current value is null
main(){
Animal animal = Animal();
print(animal.dog?.name ?? "The kiki"); // output "The kiki"
}
The function required not null
fun getName(String name){
print(name);
}
main(){
String? name = null;
getName(name); // throw an exception because the required the name argument is not null
}
The function allows null
fun getName(String? name){
print(name);
}
main(){
String? name = null;
getName(name); // output null
}
Null safety in the Constructor
Use required operator
class Animal {
String? name;
String age;
Animal({this.name,required this.age}); // The age argument not allowed to be null so you must add the required operator first
}
or use positional arguments constructor
class Animal {
String? name;
String age;
Animal(this.age, {this.name}); // age is positional argument
}
or use default value constructor
class Animal {
String? name;
String age;
Animal({this.name, this.age = "18" }); // age is positional argument
}

Understanding " ! " Bang Operator in dart

I declared a callback method inside a Stateful class like:
final void Function(int index)? onSelected;
MyBottomNavigationBar({#required this.onSelected});
and called using widget.onselected inside state class like:
widget.onSelected!(_selectedIndex);
But I'am not able to understand the actual use of ! Bang operator. I can't initialize widget.onSelected in constructor without giving ? during declaration.
My main question is "How ! bang operator is handling the null values? What's the use of it ?
The Bang operator does nothing more than tell Dart that even if we have defined some variable as a Nullable type, it will definitely not be null.
Dart's Nullable safety and Flow Analysis are here to make the developer's life easy by avoiding runtime error and catching them at compile-time itself.
When you define,
final void Function(int index)? onSelected;
You are telling dart that this variable may or may not be assigned a value and thus it may or may not be null.
Now, whenever you try to use it like this,
widget.onSelected()
Dart will warn you that, this is a Nullable typed variable hence take extra care.
If Dart didn't tell you this before hand, then you would've realized it after you ran the app and it crashed while trying to call the function.
Now, since Dart has warned you that you cannot use it like that, there are two ways to approach it.
Put a null check before using it. This way we are avoiding runtime errors.
var onSelected = widget.onSelected;
if (onSelected != null) onSelected(_selectedIndex);
This creation of a new local variable is necessary since Dart's flow analysis only works on local variable null checks and not class level variable null checks.
Use the bang operator,
widget.onSelected!(_selectedIndex)
Now the issue with this is that, you are just assuring Dart that it will never be null, but if it were actually null, it will still result in a runtime crash.
So, the Bang operator is just a way to bypass Dart's flow analysis when you have to access a nullable typed variable.
Hope this clears at least a bit of your doubt.
The bang operator works like an assert statement but in production mode too.
Let's say, this is your function.
final void Function()? foo;
You can't call it directly
foo(); // Error
Here comes a bang operator, which actually says that Dart I take the full responsibility that this function isn't null, let me proceed with the call.
foo!(); // No error
This is essentially equal to doing:
(foo as Function())();
Note:
You will run into an error if foo was null. A good approach is to check for nullability using a local variable and proceed to the call.
final f = foo;
if (f != null) {
f();
}

Checking if a value is assigned to a boolean variable in specs

In specs bdd framework(http://code.google.com/p/specs), i can check if a value is assigned to a string or not using :
SomeClass.strVal == null mustBe false
Now, suppose the class has a boolean value boolVal. How to check if a value true or false was assigned to boolVal or not ?
Please Help
Thanks
Acccording to this page, the Boolean class derives from AnyVal. Therefore boolean is a value type and no boolean variable can be null. In fact, since Scala runs atop the java pplatform, it probably has the default value of false
In general, it is not good practice to leave variable uninitialized. When a variable is declared, give it a value. This alleviates the need to do these kinds of checks.