What is "?." operator in flutter - flutter

I am using a library and in many places ?.
operator is used i am unable to understand it's purpose.
Timer _debounceTimer;
#override
initState() {
_textController.addListener(() {
// We debounce the listener as sometimes the caret position is updated after the listener
// this assures us we get an accurate caret position.
if (_debounceTimer?.isActive ?? false) _debounceTimer.cancel();

?. [Conditional member access] - Like ., but the leftmost operand can be
null; example: foo?.bar selects property bar from expression foo
unless foo is null (in which case the value of foo?.bar is null)
from Dart Language Tour (Other Operators)
TLDR: It is simply does a null check before accessing member. If left hand side of the operator is not null then it works simply as . and if it is null value then the whole thing is null.
In your example: _debounceTimer?.isActive - if _debounceTimer is null then _debounceTimer?.isActive <-> null and if _debounceTimer is not null then _debounceTimer?.isActive <-> _debounceTimer.isActive.
Also check: Dart Language tour (Conditional Expressions) for ?? and ? operator.

Related

Dart null safety !. vs ?-

what is the difference exactly between
String id = folderInfo!.first.id; //this works
and
String id = folderInfo?.first.id; //this is an error
I know ?. returns null when the value object is null but what does the !. return?
?. is known as Conditional member access
the leftmost operand can be null; example: foo?.bar selects property bar from expression foo unless foo is null (in which case the value of foo?.bar is null)
In your case, String id means id can not have null value. But using ?. can return null that's why it is showing errors.
!. is use If you know that an expression never evaluates to null.
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).
More and ref:
important-concepts of null-safety and operators.
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.
In other words !. will throw an error if the value is null and will break your function and as you know ?. will return null with no interruption.
The ! throws an error if the variable is null. You should try to avoid this if possible.
If you’re sure that an expression with a nullable type isn’t null, you can use a null assertion operator (!) to make Dart treat it as non-nullable. By adding ! just after the expression, you tell Dart that the value won’t be null, and that it’s safe to assign it to a non-nullable variable.
In your first case, you define id not nullable but when you set nullable value then throw error.
String id = folderInfo?.first.id;
In 2nd case, when you use assertion operator (!), it actually tell compiler that it must be non nullable.

Is there any difference between initializing with empty values or null value?

Is there any difference between these two part of code? (I am specially using Flutter/Dart but also interested to know about this in any other popular languages like C/C++, Java, JS, Python, etc.)
Code1:
String a = null; (in Flutter: a = null as String; )
List<T> = null; (in Flutter: List<T> = null as List<T>;)
Code 2:
String a = '';
List<T> = [];
With Dart nullsafety your first examples are invalid (assuming you meant to have a variable name on your List). If a variable can contain null the type needs a '?' suffix:
String? a = null;
List<T>? b = null;
But with or without nullsafety or '?', use of the variables in your second example won't result in runtime errors. For example a.trim(): if a is null a runtime error will occur because you're trying to call (null).trim(). In your second example no error because there is a String object to access, even though that String object is empty of characters. Same for your List: b.forEach((e) {}) when b==null a runtime error occurs because there's no Object to find forEach() - i.e. the runtime doesn't know what to do with (null).forEach(). In your second example the forEach() doesn't execute the function because List b is empty, but there's no runtime error because there's an Object to call forEach() on.
In C: NULL for pointers is usually synonymous with 0. The result of trying to access memory address NULL is "undefined" in C, because it could be valid as 0x00 in some instances like embedded systems or low-level system code, but will usually result in your process being terminated (crash).

Flutter null-safety conditionals in object methods

I'm just working through this whole null-safety mode with my Flutter project and unsure what the difference is with ? and ! in calls to object methods.
For example, the hint was to add a ! conditional. Here's an example I have right now, and I'm unsure if this should be a ? or a ! at the findNbr!.replaceAll().
Future checkItem({String? findNbr}) async {
int? x = int.tryParse(findNbr!.replaceAll('-', ''));
...
Does this mean replaceAll() will not run if findNbr is null?
Or should it be a ? instead? findNbr?.replaceAll()
EDIT: I just noticed I cannot use findNbr?, it's telling String? can't be assigned parameter String.
Or does it mean I say it's not null and run it anyway?
For your information, I have not come close to running my app yet so I have no idea if it even works. But I figure I better know what it's doing before get too much more done. I'm still in the process of converting everything and there's 75-100 dart files. I'm not sure I get the point of it all to be honest, because I just add ? to everything, so its all nullable anyway.
Future checkItem({String? findNbr}) async {
int? x = int.tryParse(findNbr!.replaceAll('-', ''));
...
Does this mean replaceAll() will not run if findNbr is null?
Correct. If findNbr is null, then findNbr! will throw a runtime exception. That would be bad, especially since checkItem's function signature advertises that findNbr is allowed to be null, and therefore it would violate callers' expectations.
Or should it be a ? instead? findNbr?.replaceAll()
EDIT: I just noticed I cannot use findNbr?, it's telling String? can't be assigned parameter String.
You can't use findNbr?.replaceAll(...) because if findNbr is null, then it would be invoking int.tryParse(null), but int.tryParse is not allowed to take a null argument.
What you need to do is one of:
Make findNbr no longer optional:
Future checkItem({required String findNbr}) async {
int? x = int.tryParse(findNbr.replaceAll('-', ''));
...
Allow findNbr to be optional but have a non-null default value:
Future checkItem({String findNbr = ''}) async {
int? x = int.tryParse(findNbr.replaceAll('-', ''));
...
Allow findNbr to be optional but explicitly decide what to do if it is null. For example:
Future checkItem({String? findNbr}) async {
int? x = findNbr == null ? null : int.tryParse(findNbr.replaceAll('-', ''));
...
I'm not sure I get the point of it all to be honest, because I just add ? to everything, so its all nullable anyway.
If you blindly add ? to all types and add ! to all variables, then yes, null-safety would be pointless: doing that would give you the same behavior as Dart before null-safety.
The point of null-safety is to prevent things that shouldn't be null from ever being null. You could have written such code before, but without null-safety, that meant performing runtime null checks (e.g. assert(x != null);, if (x != null) { ... }, or relying on a null-pointer-exception to crash the program if null was used where it wasn't expected). Null-safety means that such checks now can be done at build-time by static analysis, which means that errors can be caught earlier and more completely. Furthermore, whereas previously functions needed to explicitly document whether arguments and return values were allowed to be null (and inadequate or incorrect documentation could be a source of errors), now they're self-documenting in that regard. It's just like using int foo(String s) versus dynamic foo(dynamic s); using strong types catches errors earlier and better describes the function's contract.
I recommend reading Understanding Null Safety if you haven't already done so.
I would like to advice you to use the ! operator, also the called bang operator, as little as possible. You should only use this operator when the dart analyser is wrong and you know for 100% that the value will never be null.
Below is an example of where the dart analyser would be wrong and you should use the bang operator.
// We have a class dog with a nullable name.
class Dog {
String? name;
Dog({this.name});
}
void main() {
// We create a dog without a name.
final dog = Dog();
// We assign the dog a name.
dog.name = 'George';
// The dart analyser will show an error because it can't know if the
// name of the object is not null.
//
// Will throw: `A value of type 'String?' can't be assigned to a
// variable of type 'String'`.
String myDogsName = dog.name;
// To avoid this, you should use the bang operator because you `know` it
// is not null.
String myDogsName = dog.name!;
}
The ? operator simply tells Dart that the value can be null. So every time you want to place a ? operator, ask yourself, can this value ever be null?
The null safety features in Dart are mainly created for helping the developer remember when a value can be null. Dart will now simply tell you when you made a variable nullable in order to force null checks or default values for example.

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!

how to handle empty array for text widget in build method?

How would I handle the objectA[0].name (a string) in the build method if the array is empty?
Text(objectB.objectC.objectA[0].name),
Assuming the array is objectC you can do something like:
Text(objectC.isEmpty? "" :objectC[0].name)
You can read more about ternary operators in dart here
There isn't a great way to do this inline but with a simple extension method to return the original null or the iterable depending on whether the item is null or empty you can make it work.
First
Extension Method
(requires dart v2.7 - update in your pubspec.yaml file)
extension IterableExtension<T> on Iterable<T> {
Iterable<T> get nullWhenEmpty =>
this == null || this.isEmpty ? null : this;
}
Second
To handle null values while you're traversing an object you can use the Dart's conditional member access operator (?.). This operator will only continue with the right-hand side if the left-hand side of the operator is not null. Use the elementAt method on a an iterable to be able to use the ?. operator in the chain. Then, use the ?. operations with the if null operator (??) to get your default value.
Solution
final String value = objectB?.objectC?.objectA?.nullWhenEmpty?.elementAt(0)?.name;
Text(value ?? 'Default Text');
You can, of course, inline the above code instead of using an additional variable.
Resources
Dart Language Tour: Other Operators
Dart Language Tour: Classes
Maybe checking if the list has an element, using isNotEmpty
child: (objectB.objectC.objectA.isNotEmpty)
? Text(objectB.objectC.objectA[0].name)
: Container(),