Flutter null-safety conditionals in object methods - flutter

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.

Related

Unnecessary usage of bang operator

I have a problem understanding, and living with, the excessive use of bang operators in dart/flutter.
Consider this example:
if(model != null && model!.someValue != null) {
print(model!.someValue!);
}
The first condition check is verifying that the model is not null. In the second condition I have to put in a bang operator after model, else the compiler gives me an The property 'someValue' can't be unconditionally accessed because the receiver can be 'null' error. But why is this necessary? I´ve just checked the variable! And same goes for the print(model!.someValue!); line.
Another example where I have these classes:
class GeoPosition {
double lat = 0;
}
class Wrapper {
GeoPosition? position;
}
...
Wrapper wrapper = Wrapper();
wrapper.position = GeoPosition();
wrapper.position!.lat = 1;
Now why do I need to put this bang operator (or ? operator) after position? I´ve just created a new instance of GeoPosition in the Wrapper instance - position cannot be null.
My best guess is that the compiler cannot see or understand the current context of the class. But in Typescript the linter is smart enough to know when these operators are not necessary.
I know that I can create local variables from the properties that I am trying to access, but this would be just as ugly ;)
So why are ! and ? necessary in these (and many other) situations? And is there anything I can do about it?
This is what happens with nullable properties (hence the message you get). It is explained here: Understanding null safety: Working with nullable fields.
You should be able to work around this issue by declaring the field as late, as in
class Wrapper {
late GeoPosition position;
}
Wrapper wrapper = Wrapper();
wrapper.position = GeoPosition();
wrapper.position.lat = 1;
This will remove the need to add a bang to every access to position The compiler will add a non-null check at appropriate places. Of course, the program will fail if you don't assign a non-null value before accessing the field.
If explained in Late variables
Nullable properties of a class can still be null between two access.
For your first example you can extract the variable then check it:
var someValue = model?.someValue;
if(someValue != null) {
print(someValue); // not null
}
For your second example an elegant way will be the .. operator :
Wrapper wrapper = Wrapper();
wrapper. Position = GeoPosition()..lat = 1;
If you want the GeoPosition to be not null you have to make it not nullable:
class Wrapper {
GeoPosition position;
Wrapper(this.position);
}
You can set the position field final to be immutable.
Be careful with the late keyword, if you forget to init the field you've got a crash like the ! operator.

Syntactic sugar for calling a function with a nullable value that might return null

I am looking for syntactic sugar with null-safe-operators to do this:
map["key"] == null ? default : (int.tryParse(map["key"]!) ?? default)
This however accesses map twice and requires default twice.
Better, but verbose code that also compiles:
String? val = map["key"];
int? res;
if (val != null) { res = int.tryParse(val) }
res ??= default;
In essence I am looking for a way to only call a function if the parameter is not null. E.g. in kotlin you could do (pseudocode)
map["key"].let{ int.tryParse(it) } ...
I found this related question, however writing helper methods is even more verbose and I cannot edit the function to take nullable parameters.
What i would love to see for dart (but afaik this does not exist):
int.tryParse(map["key"]?) ?? default;
I.e. func(null?) => null, no matter what func is, or sth similar.
Is there a smooth way to do that or is the verbose way for now the "accepted" way?
EDIT
I am aware of the int.tryParse("") or int.tryparse(default.toString() hacks, however both are somewhat naughty as they will call tryParse either way instead of skipping it if the value is null anyways. (imagine replacing tryParse with a very expensive factory method)
There is two things you can do:
int.parse(map["key"] ?? default.toString());
This would work if you are sure that map["key"] can be parsed if it is not null.
If you have to do this operation a lot you can also write your own extension function and then use the null-safe operator:
extension NullSaveParser on String {
int? tryToInt() {
return int.tryParse(this);
}
}
And then use this:
map["key"]?.tryToInt() ?? default;
This extension is neccessary because there is currently no way in dart to not call a method if an argument would be null, only if the object that you are calling it on is null can be caught.
I hope this helps.
It's sort of a hack and maybe ugly but for this case you might want to write something like
int.tryParse(map["key"] ?? '') ?? default
That is, put a fallback value inside the tryParse that you know will make the tryParse return null

Can't assign non-nullable type to a nullable one

error: The argument type 'Future<List<GalleryPictureInfo>>' can't be assigned to the parameter type 'Future<List<GalleryPictureInfo>>?'.
Is this Dart Analysis or me? The project still compiles.
Upd. Added code example
FutureBuilder<List<GalleryPictureInfo>>(
future: derpiService.getListOfImages(),
//other code
);
#override
Future<List<GalleryPictureInfo>> getListOfImages(arguments) async {
List<GalleryPictureInfo> listOfImages = [];
var searchImages = await getSearchImages(tags: tags, page: page);
//adding images to List
return listOfImages;
}
It's something with FutureBuilder actually. I should've mention this.
Upd. "Fixed" with // ignore: argument_type_not_assignable
Looks like a problem with Dart Analysis for now
Upd. Error
It actually is an error which is pretty self explanatory.
The acutal error comes because of null safety in dart.
For ex:
void main(){
var number = getNumber(true);
int parsedNumber = int.parse(number);
print(parsedNumber);
}
String? getNumber(boolean value) {
if (value){
return null;
} else return "1";
}
So here, getNumber function either returns null or "1" depending upon the value of value variable. So, number variable's type is String?.
But the error shall arise in the next line when you try to call int.parse(). int.parse function takes an argument which should be a String but the value passed in the function is of type String?. So if we pass null in int.parse it shall throw an error.
That's why Dart analysis makes it easier to identify such cases by telling us that the value can be null and it might throw.
However the code depends upon your actual code of your project. It says that you are passing Future<List<GalleryPictureInfo>>? which is of nullable type to a function which requires Future<List<GalleryPictureInfo>>. So, before passing the value you might want to check if the value you are passing is not null.
If you are sure that the value can never be null then if for ex: if you are passing a variable called value, you might wanna try someFunctionWhereYouPassValue(value!)
That ! means that you are sure that the value will never be null.
For more details about null safety you can see:
https://dart.dev/null-safety/understanding-null-safety

Error to use assignment operators ==? in Dart

I am learning Dart and practicing with this video I came across this way of assigning a value when the variable is null
void main() {
int linus;
linus ??= 100;
print(linus);
}
When trying to test the code in VSCode I get the following error which I cannot identify its origin since from what I understand, I am using what is indicated in the documentation (and the video tutorial).
The non-nullable local variable 'linus' must be assigned before it can be used.
Try giving it an initializer expression, or ensure that it's assigned on every execution path.
The Dart Language now supports a new feature called sound null safety. Variables now are non-nullable by default meaning that you can not assign a null value to a variable unless you explicitly declare they can contain a null.
To indicate that a variable might have the value null, just add ? to its type declaration:
int? linus;
So, remember: every variable must have a value assigned to it before it can be used. As in your example, linus variable is non-nullable by default ,null-aware operator has nothing to do because it will assign value to linus if it is null.So, linus gets no value and thus it can't be used in the print function.
So to solve this, you can do this:
void main() {
int? linus; //marks linus as a variable that can have null value
linus ??= 100;
print(linus);
}
To know more about null safety
The Documentation is Non-Null Safety and you are trying in Null safety version
Please check below code
void main() {
int? linus;
linus ??= 100;
print(linus);
}

How to use optional in Dart for null safety

I found this package that implements Optional for Dart: https://pub.dev/packages/optional/example
On the examples, it does things like this:
void filterExample() {
final hello = Optional.of('hello');
final world = Optional.of('world');
final name = Optional.of('harry');
for (var o in [hello, world, name]) {
final filtered = o.filter((v) => v.startsWith('h'));
print(filtered.isPresent);
} // prints "true", "false", "true"
}
But how do I force a variable to be Optional of some type? I wanted to have Optional<String>, Optional<int>, etc, but I'm forced to give a value in the beginning.
The closest I can think is
final anEmpty = Optional.ofNullable(null);
which is already in the example, but what is a Nullable? If I do like this, I cannot constraint the value to be a String or int, it can be changed to anything. I want to stick to strong typing while using Optional.
If this is not possible with this library, then how can I make my own simple Optional type that supports templates so I can have Optional<String>, Optional<int>, etc?
The behavior you are looking for (doing something if null and something else otherwise) can be achieved using the ?? operator.
This is basically a null check which equals the left hand if not null and right hand if null. Example: doSomething(myString ?? "Default value of my string");
Does this answer your question? Sorry, I haven't used that package and don't see a need to.