Why is manual null check required when ? operator is there in Flutter? - flutter

String playerName(String? name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
}
? checks whether name is null or not, then why is special if (name != null) { condition required?

The String? name means that the parameter name is nullable as you can see lower in the code the if statement then checks if your parameter is not null.
Dart docs definition:
If you enable null safety, variables can’t contain null unless you say they can. You can make a variable nullable by putting a question mark (?) at the end of its type. For example, a variable of type int? might be an integer, or it might be null. If you know that an expression never evaluates to null but Dart disagrees, you can add ! to assert that it isn’t null (and to throw an exception if it is). An example: int x = nullableButNotNullInt!
Link to docs

Related

In dart what is the difference between ? and ! for nullable types?

I am new to Dart and Flutter.
In dart what is the difference between using ? and ! for null-able types?
validator: ((value) {
if (value?.isEmpty) {
return "Field is required";
}
return null;
}),
validator: ((value) {
if (value!.isEmpty) {
return "Field is required";
}
return null;
}),
Thanks in advance!
Good topic about it : What is Null Safety in Dart?
But in short, you use "?" when you want to allow the value to be null and use it accordingly, like this:
String? test;
if (test?.isEmpty == true) { // And is not null, but you don't need to check it
// If null, will never pass there but without error
}
And use "!" when you want to be sure to have a non nullable value, like this:
String? test;
if (test!.isEmpty == true) { // Will throw an error
...
}
the difference between the two,one can be null initially, but the other cannot.
I hope you understand in the example below.
To specify if the variable can be null, then you can use the nullable type ?
operator, Lets see an example:
String? carName; // initialized to null by default
int? value = 36; // initialized to non-null
value = null; // can be re-assigned to null
Note: You don’t need to initialize a nullable variable before using it. It is initialized to null by default.
The Assertion Operator (!)
Use the null assertion operator ( ! ) to make Dart treat a nullable expression as non-nullable if you’re certain it isn’t null.
int? someValue = 30;
int data = someValue!; // This is valid as value is non-nullable
In the above example, we are telling Dart that the variable someValue is null, and it is safe to assign it to a non-nullable variable i.e. data
I hope you understand????
As for your example;
if you notice, the validator {String? value} value can initially be null. but the only difference between both works in the code you wrote will be the running cost. '?' it will cost some time when you define it again. because it is already stated in the function that it will be null as a start.
It's a good question and the answer is here as a person.
'?' it means it will get value later or it can be null( initially or at any instance) for example
String? carName;
'!' it means you are going to receive the value and it can not be null. it will check the value if the value is null it will give exception.
have a look on example for clear difference:
List? blocks;
...
// you are not sure blocks variable is initialized or not.
// block is nullable.
final Block? block = blocks?.first;
// you are sure blocks variable is initialized.
// block is not nullable.
final Block block = blocks!.first;
hope you got it if yes accept the answer or comment me if you have question

How can I make null-safety assertions to avoid using null check (!) or conditional (?) operators in Flutter?

Dart compiler does not understand that the variable can not be null when I use it inside an if (x != null) statement. It still requires to use conditional ? or null check ! operators to access one of the variable's fields or methods.
Here is an example:
String? string;
void test() {
if (string != null) {
print(string.length);
}
}
This produces a compile-time error and says that
The property 'length' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!').
However, the receiver actually can't be null since it's wrapped with if (string != null) block. Accessing the field with string?.length or string!.length works fine but it can be confusing when I need to use different fields or methods of the variable.
String? string;
void test() {
if (string != null) {
print(string.length);
print(string.isNotEmpty);
print(string.trim());
print(string.contains('x'));
}
}
All of these statements raise the same error. I also tried putting assert(string != null); but the compiler still does not understand that the string is not null.
To give a more sophisticated example;
User? user;
#override
Widget build(BuildContext context) {
if (user == null) {
return Text('No user available');
} else {
return buildUserDetails();
}
}
Widget buildUserDetails() {
return Column(
children: [
Text(user.name),
Text(user.email),
],
);
}
This is still a problem for the compiler.
However, the receiver actually can't be null since it's wrapped
And that assumption is plain wrong.
Your variable is a global variable and any other part of your program, through multi-threading or other shenanigans can slip in between your if and the next line and change the variable.
That is why only local variables can be promoted to their non-null equivalent when the compiler proves that they cannot be null in certain code execution branches like an if.
The following will work perfectly fine, because you are operating on a local variable that the compiler can be sure won't be changed by outside operations:
String? string;
void test() {
final local = string;
if (local != null) {
// here, local was promoted from "string?" to "string"
// since the "if" makes sure it is not null AND
// the variable is not accessible to anything but this
// function, so it cannot be changed from the outside
// and is contrained to the sequential flow of this method.
print(local.length);
}
}
These are for sound null safety. Thats why whenever you start calling a functions / accessing it makes sure that the variable is not null by using ! or provide other case for null using ?.
Suppose for following case :
if (string != null) {
string=null; // Or through other function xyx() {string=null;} string becomes null then your if condition is void
print(string.length);
print(string.isNotEmpty);
print(string.trim());
print(string.contains('x'));
}
// Still sound null safety that's why above is not allowed
if (string != null) {
string=null; // Or through other function xyx() {string=null;} string becomes null then your if condition is void
print(string!.length);
print(string!.isNotEmpty);
print(string!.trim());
print(string!.contains('x'));
}
So for sound null safety it is required to be checked if string is not null before accessing it.
As per your comments you need to assign this nullable string to a non nullable string (~isdatablank~ means String was null) and proceed
String? string;
String s=string??"~isdatablank~";
if (s != "~isdatablank~") {
print(s.length);
print(s.isNotEmpty);
print(s.trim());
print(s.contains('x'));
}

How to use the Null assertion operator with instance fields

I'm having trouble understanding how to type promote an object's field if it is nullable. Let's say I had the following Comment class and tried to access its one nullable field:
class Comment {
final String? text;
Comment(this.text);
}
void main() {
final comment = Comment("comment");
if (comment.text!= null) {
String text = comment!.text;
}
}
The Dart compiler would give me an error for trying to assign a nullable variable to a non-nullable variable. From what I've gathered from looking into this topic, it's impossible to have type promotion with instance variables because instance variables can be modified which can then break the sound null-safety. I've seen the Null assertion operator (!.) being used in these circumstances, however it doesn't seem to work with fields, only with methods.
With dart null-safety, how should I go about assigning a nullable field such as String? to a non-nullable variable (String). To the same effect, how should I go about passing a nullable field to a function that requires a non-null argument.
You can fix it in different ways:
Use local variable (recommended)
final local = comment.text; // <-- Local variable
if (local != null) {
String text = local;
}
Use ?. and provide a default value
String text = comment?.text ?? 'Default value';
Use Bang operator !
if (comment.text != null) {
String text = comment.text!; // <-- Bang operator
}
Note: Bang operator can result in a runtime error if the text was null.

Dart, Identifier with exclamation mark in the back

Recently I have been developing mobile aplication with flutter, when I looking at the source code for TickerProvider I see these lines:
mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
Ticker? _ticker;
#override
Ticker createTicker(TickerCallback onTick) {
...
_ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by $this' : null);
return _ticker!;
}
...
}
I'm interested with this line:
return _ticker!;
I have seen boolean identifier with exclamation mark in the front meaning that it will return the opposite value of it, but I never see this one. Can someone tell me what this does?
It's part of the null safety that Dart have.
You can read about it here
If you’re sure that an expression with a nullable type isn’t null, you can add ! to make Dart treat it as non-nullable
Example:
int? aNullableInt = 2;
int value = aNullableInt!; // `aNullableInt!` is an int.
// This throws if aNullableInt is null.
For betrer undestanding (by analogy with the action of the algorithm itself).
This operator acts as the following inline internal function (at least similarly):
T cast<T>(T? value) {
if (value == null) {
throw _CastError('Null check operator used on a null value');
} else {
return value;
}
}
P.S.
Forgot to add.
Based on the error messages that are generated at runtime, we can conclude that this operator is called the Null check operator, which may mean that it checks the value for null and throws an exception if the value is null.
No magic!

Should you check runTimeType of variables in fromJson functions as best practice?

Pretty basic question, but wondering what the best practice is and can't seem to find any references to this on SO or elsewhere.
Should you check the runTimeType of properties before you attempt to store them when you parse fromJson even if you are confident it should never be anything but the type you think, or null? Or do we just accept the error if this highly unlikely event ever happens?
Thanks !
factory SomeClass.fromJson(Map data) {
if (data == null) return null;
String someString = data['someString']; //no runTimeType check
int someInt = data['someInt']; //no runTimeType check
try {
assert(someString != null, 'Some String was null in Some Class Json');
assert(someInt != null,
'Some Int null in Some Class Json');
} catch (e) {
return null;
}
return SomeClass(someString: someString, someInt: someInt);
}
It will be really nice to check the runtime type of the variable as well, as it will prevent any potential app crash when the database is updated in the future.
As far as the null checks are concerned, I personally pass in a default value like for String I store an empty string as the default value instead of null.
String someString = data['someString'] ?? '';
Even if the database returns a null value, our app should be able to handle those conditions.
The most common type of error that I've personally experienced is during the parsing of data. Type String is not a subtype of int. Something like that. So, I guess it would be nice to check the runtime types of values returned from the backend.