I've enabled the dart 2.8 nullsaftey experiment.
I've converted my app to nullsaftey but its using an old pre-nullsafety package.
The problem is that the old package has a method which can return null:
/// Returns the environment variable with [name] or null if it doesn't
/// exist
String env(String name);
Which is used as follows:
var home = env('HOME');
If the HOME environment variable is missing, env
returns null.
The problem is that env
is declared as returning a String
.
So when I write
var home = env('HOME');
home ??= '/home';
I get an error:
The operand can't be null, so the condition is always false.
Try removing the condition, an enclosing condition, or the whole conditional statement.
Given that all the nullsaftey release announcements say you can use nullsaftey with older packages, I'm guessing there is some way to declare an imported packages as non-nullsafe.
Problem is that I can't find any documentation on how to do this.
Use the null assertion operator ( ! ) to make Dart treat a nullable expression as non-nullable if you're certain it isn't null. is null, and it is safe to assign it to a non-nullable variable i.e.
Copy: --no-sound-null-safety and add into "additional run args". Run your app with "Run/Play" Button or from "Run" Menu. In this way, you can solve "Error: Cannot run with sound null safety, because the following dependencies don't support null safety" error on Flutter project.
null safety has not been released yet! that is why you need to provide the experiment flag.
By default, whether or not null safety is supported in a library is determined by its language version. Any language version 2.8 or lower counts as opted out of null safety, and 2.9 or higher (subject to change) is opted in. The language version itself can come from one of two places:
name: foo
env:
sdk:
">=2.8.0 <3.0.0"
// @dart=2.8
class Foo {}
The language override comment will take precedence over the SDK constraint, but only within the single library where it is declared.
The problem you are having is reproducible without different packages or incorrect language versions though, and has to do with the interaction between null-safe and non-null-safe code. Consider the following example:
// @dart=2.8
String foo() {
return null;
}
// @dart=2.9
import 'a.dart';
void main() {
var value = foo();
value ??= 'asd';
}
The return type of foo
doesn't become String?
, instead it gets tagged as String*
- this is known as a legacy type. A legacy type is treated as a non-null type in opted in libraries. The goal of legacy types is to make it easier to migrate to null-safety through an in-order migration
Consider the example below:
// @dart=2.9
void foo(String value) {
// do something with non-null String.
}
// @dart=2.8
import 'a.dart';
void main() {
foo(getStringFromAPI());
}
While foo
requires a non-null string, it isn't possible for the entry-point to actually pass it one - since it has not opted in yet. Without the treatment of legacy types as non-nullable types, it would not be possible to gradually migrate - because all libraries would need to be updated at once, or only updated to accept nullable types.
By calling code that has not been migrated to null-safety from a null safe library, you are increasing the risk that you will be broken when that dependency eventually migrates. In you example, if home
was treated as non-nullable then updating to a version of the dependency with an updated return value of String?
would cause a compilation error.
For your specific case, I would recommend specifically annotating the type of home
as String?
. This is a perfectly valid type annotation, since in general T
and T*
are always assignable to T?
. It also more correct, since you know the API can return null.
String? home = env('HOME');
home ??= '/home';
Null safety has released, yay! The first version of Dart with null safety enabled by default ended up being 2.12
and not 2.9
as documented in the question above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With