Understanding " ! " Bang Operator in dart - flutter

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();
}

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.

Flutter: Late Initializing a "widget-dot" Class-level Variable in a Stateful Widget -- What's Going on?

Consider the following class level property inside the state class of a stateful widget:
int myInt = widget.int;
Android Studio informs that: "The instance member 'widget' can't be accessed in an initializer."
(I understand what this error means).
So then if we add the late keyword, it appears to be fine:
late int myInt = widget.int;
However... this is surprising to me that I’m allowed to do all that in one line — I thought that late variables had to be not set/set as null:
late int myInt;
... and then assign inside onInit.
Since I didnt declare when to assign it, I dont know when the assignment takes place.
The question is:
Is the one-liner “late int myInt = widget.int;” exactly equivalent to assigning it myself in the initState method?
The late keyword in Dart has 2 distinct usages, melt into a single keyword.
The first usage, e.g. late int i:
This usage is well-known: delay assigning a value until later. This is most commonly used to make a field non-nullable, even though you might not have the value right away. I'm sure you are familiar with this usage.
The second usage, e.g. late int i = 0:
This is to delay the value calculation until the field is being accessed. This is useful when the value is expensive to calculate, so you might want to delay its calculation until it's needed for the first time. It's stated on the official documentation:
When you do this, the initializer becomes lazy. Instead of running it
as soon as the instance is constructed, it is deferred and run lazily
the first time the field is accessed. In other words, it works exactly
like an initializer on a top-level variable or static field. This can
be handy when the initialization expression is costly and may not be
needed.
So basically, depends on whether you assign a value right away (on the same line), Dart will decide which of the 2 usages you are using. If you write late int i it will be the first usage, if you write late int i = 0 or late int i = calculateValue() it will be the second usage: delay the calculation until when the field i is accessed for the first time. It's like lateinit in Kotlin or lazy in Swift.
Now back to your case. By assigning a value on the same line as the late keyword, you are using the second usage, basically "lazy init" until the field is accessed for the first time. By the time it's accessed, this class would've been instantiated, so (by that time) you are allowed to use the this keyword.
In the first case Android studio throws that error because int myInt requires a value the moment you are declaring it.
In that particular moment, in the Statefull widget state, the widget object is not accessible.
In the second case:
late int myInt = widget.int;
That is a valid one line declaration and assignment of the variable, but the effect is a bit different from the onInit alternative.
The late keyword works in a lazy way.
Instead of running as soon as the instance is built, it run the first time the field is used. In that moment the widget object will be accessible.
Take a look at the answer to this question, it can be helpful: here
Assigning the value inside the onInit guarantees that the value is actually assigned only once when the widget is initialized.
widget.xxx corresponds to the value of xxx of the instance of a widget, ie the widget once it exists.
So when you use widget.xxx in initialisation of the widget, the var xxx does not exists.
That's why the dart compiler tell you The instance member 'widget' can't be accessed in an initializer .
By adding the keyword late in front of the declaration, you tell the compiler that this variable will be defined later.
But be careful, it will then really have to be defined later (in initState for example) and in any case before any use.
This error comes from the fact that dart is now a null safety aware language.
That is to say a language that strives to ensure that no variabales can have a null value. This is for reasons of code quality and greater code security.

why is the null-check of an outer variable isn't enough in dart?

Why does the below code give an error?
Function? fob;
void someMethod() {
if(fob != null) {
fob();
}
}
Why is this null-check not enough and fob(); here gives an error? What could be happening between the if check and the call to the function that it could be null again?
I know this works when I declare a local variable to the function, but I just want to understand why dart works the way it does.
Since this variable is not an inline variable, we can't be sure that it will not change between checking and using. You may be calling another function inside your condition block and that function sets that global variable to null. So to have sound null-safety type promotion only works on inline variables.
In your case, you can use fab?.call() without checking the variable isn't null or fab!() inside your condition block. Read more here.

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 am getting the following error NoSuchMethodError was thrown building Consumer<AppModel>(dirty, dependencies: [InheritedProvider<AppModel>]): [duplicate]

I have some code and when I run it produces an error, saying:
NoSuchMethod: the method 'XYZ' was called on null
What does that mean and how do I fix it?
Why do I get this error?
Example
As a real world comparison, what just happened is this conversation:
Hey, how much gas is left in the tank of the car?
What are you talking about, we don't have a car.
That is exactly what is happening in your program. You wanted to call a function like _car.getGasLevel(); but there is no car, the variable _car is null.
Obviously, in your program it might not be a car. It could be a list or a string or anything else really.
Technical explanation
You are trying to use a variable that is null. Either you have explicitly set it to null, or you just never set it at all, the default value is null.
Like any variable, it can be passed into other functions. The place where you get the error might not be the source. You will have to follow the leads from the actual null value to where it originally came from, to find what the problem is and what the solution might be.
null can have different meanings: variables not set to another value will be null, but sometimes null values are used by programmers intentionally to signal that there is no value. Databases have nullable fields, JSON has missing values. Missing information may indeed be the information itself. The variable bool userWantsPizzaForDinner; for example might be used for true when the user said yes, false when the user declined and it might still be null when the user has not yet picked something. That's not a mistake, it's intentionally used and needs to be handled accordingly.
How do I fix it?
Find it
Use the stack trace that came with the error message to find out exactly which line the error was on. Then set a breakpoint on that line. When the program hits the breakpoint, inspect all the values of the variables. One of them is null, find out which one.
Fix it
Once you know which variable it is, find out how it ended up being null. Where did it come from? Was the value never set in the first place? Was the value another variable? How did that variable got it's value. It's like a line of breadcrumbs you can follow until you arrive at a point where you find that some variable was never set, or maybe you arrive at a point where you find that a variable was intentionally set to null. If it was unintentional, just fix it. Set it to the value you want it to have. If it was intentional, then you need to handle it further down in the program. Maybe you need another if to do something special for this case. If in doubt, you can ask the person that intentionally set it to null what they wanted to achieve.
simply the variable/function you are trying to access from the class does not exist
someClass.xyz();
above will give the error
NoSuchMethod: the method 'xyz' was called on null
because the class someClass does not exist
The following will work fine
// SomeClass created
// SomeClass has a function xyz
class SomeClass {
SomeClass();
void xyz() {
print('xyz');
}
}
void main() {
// create an instance of the class
final someClass = SomeClass();
// access the xyz function
someClass.xyz();
}