Why default value is needed when variable is annotated with #required? - dart

void main() {
foo(bar: 1);
}
void foo({#required int bar}) {} // Error
Error:
The parameter 'bar' can't have a value of 'null' because of its type, and no non-null default value is provided.
I am annotating bar with #required and it is also non-nullable. That means I'll always have to provide bar a non-null value. So, why does the compiler ask me to provide a default value?

#required is the old annotation tag coming from the meta package and was introduced as a way to give warnings from the analyzer. With NNBD this has been changed to a keyword called required. You can read more about this keyword in the following link:
https://dart.dev/null-safety/understanding-null-safety#required-named-parameters

Related

What is the ambiguate function doing?

This is a function in the main.dart file of the just_audio example. I don't understand what's going on with the "ambiguate" line. I understand the bang operator in this context casts to the "underlying type" but in this case there is no underlying type, I don't think. The underlying type is <T?>. I'm only familiar with what that means when I see it in documentation as "a type goes here." If it's in actual code, not sure what it's doing.
void initState() {
super.initState();
ambiguate(WidgetsBinding.instance)!.addObserver(this);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.black,
));
_init();
}
The ambiguate function from common.dart in the same lib folder:
T? ambiguate<T>(T? value) => value;
https://github.com/ryanheise/just_audio/tree/minor/just_audio/example/lib
The ambiguate function casts a value to its nullable type, so an int is cast to int?.
That allows using the ! (null assert) operator on the value without a warning, because a value of type int cannot be null, so you don't need to assert that it isn't.
The reason for doing so is that in a program which also contains non-null-safe code, the value can actually be null at runtime.
Or because you don't actually know whether the type will be int or int?, because it changed between versions of a library, and you don't want to lock yourself to only the newest version.
Which means that the only reason to use the function is that you expect your code to run against two different and incompatible versions of the same library, one null-safe or non-null-safe where a function can return null, and a newer null-safe version where the function cannot return null and is typed as such.
Then you can do ambiguate(uncertainValue)?.doSomething(), and it does something if the value isn't null, and it compiles against both versions without warning.
If you are not trying to make your code work against two different versions of the same library, which differ in nullability-behavior, then don't use ambiguate.
Even then, consider whether it'd just be easier to require the new version of the library, and lean into null safety.
(This particular use seems unnecessary. Doing ambiguate(something)!.method() will throw an error if the value is null, but so will something.method(), which will also not give any warnings. Well, unless the other version of the library is null safe and returns a nullable value, but then you shouldn't be using ! on it.)

Couldn't infer type parameter 'TSelected' in Dart null safety migration

When I was migrating my dart code to null safety I am getting the following analysis error. I would appreciate if someone can provide a solution and explain the problem in simple terms.
1 analysis issue found:
error • Couldn't infer type parameter 'TSelected'.
Tried to infer 'TSelected' for 'TSelected' which doesn't work:
Type parameter 'TSelected' is declared to extend 'Comparable<TSelected>' producing 'Comparable<TSelected>'.
The type 'TSelected' was inferred from:
Parameter 'selector' declared as 'TSelected Function(T)'
but argument is 'TSelected Function(T)'.
Consider passing explicit type argument(s) to the generic.
Generic extension for sorting:
extension IterableExtensions<T> on Iterable<T> {
Iterable<T> sortBy<TSelected extends Comparable<TSelected>>(TSelected Function(T) selector) =>
toList()..sort((a, b) => selector(a).compareTo(selector(b)));
Iterable<T> sortByDescending<TSelected extends Comparable<TSelected>?>(TSelected Function(T) selector) =>
sortBy(selector).toList().reversed;
T? get firstOrNull {
return isEmpty ? null : first;
}
}
Usage Example:
Model? get lastDateModel => modelList.sortByDescending((val) => value.date).firstOrNull;
Model :
class Model {
final Id? id;
final DateTime? date;
Model({
this.id,
this.date,
});
}
The sortBy method expects a type parameter that extends Comparable<TSelected>, but the sortByDescending method expects a type parameter that extends Comparable<TSelected>?.
See the difference? That last ? makes is possible to pass a null value instead of a Comparable<TSelected>, because you added it into the descending method but not into the original, when you call the original from the descending method, dart doesn't know what to do if you decided to pass null as an argument, so it can't compile.
So the fix is to either add a ? on the sortBy method or remove the ? from the sortByDescending either solution will require some refactoring to make sure the methods still work.

How to simulate undefined in Dart

In JS, there is undefined and null. Undefined means "no value", null means "value equivalent to emptiness".
In Dart however (and possibly in other languages, I don't know, but right now I'm using Dart), there is no undefined, only null. Therefore it is impossible to make the distinction between a value equivalent to emptiness and the absence of value.
Is there a standard way of simulating this difference in Dart?
No. The null value (of type Null) is the only built-in value that represents the absence of a value. It does not distinguish on why the value is absent.
It's also (almost) the only type you can combine with another type directly in the type system. With null safety, you'll be able to write int? for int-or-Null.
If you want another marker for absence, you can use (or write) an Option type like:
abstract class Option<T> {
final T value;
bool get hasValue => true;
Option(this.value);
factory Option.none() => const _OptionNone();
}
class _OptionNone implements Option<Never> {
const _OptionNone();
bool get hasValue => false;
T get value => throw UnsupportedError("None option has no value");
}
Then you can represent either value or no value.
(There are existing implementations of such a class, for example in the quiver package).

Dart 2.7 migration for typedef with Null type parameter

I'm trying to migrate some old dart 1.25 code to 2.7 on windows.
I face some errors and I don't understand how to fix these.
The code used some Null type parameters in a typedef like this :
typedef dynamic PropertyGetter(Null object);
I don't understand exactly why this was coded like this, but it worked on 1.25.
This typedef was use to get a property on an any passed type of object which may be Null I suppose.
I also suppose the Null type parameter had his own reason to be there.
But now, with dart 2.7, when executing the code I've got errors like this :
type 'XXXType' is not a subtype of type 'Null'
Any idea how the typedef code can be fixed ?
--- Edit 1 ---
Here is a test case to experiment with the problem :
typedef int PropertyGetter(Null object);
void main() {
testTypedef(test1, 2);
testTypedef(test2, 'foo');
}
void testTypedef(PropertyGetter pg, param){
pg(param);
}
int test1(int val) {
print('val:$val');
return val;
}
int test2(String val) {
print('val:$val');
return 0;
}
console error message :
Unhandled exception:
type 'int' is not a subtype of type 'Null'
You almost certainly don't want to be using Null as a type here as a parameter of type Null can only accept null as an argument which is pretty useless.
If you're looking to define a typedef for a function that can take in any object, you should either use dynamic or Object as the type. However, if you use Object, you'll need to know what type to cast the object to before you can access any properties of the underlying object. I'd recommend using dynamic if you're just looking for a quick fix.

I want to use named parameters in Dart for clarity. How should I handle them?

TL;DR: Named parameters are optional as a result of a conscious design choice. Short of having official language support, is there any way to enforce (and inform) required named arguments?
I find it extremely useful to use named parameters when defining a class. Take, for instance, an Ability in an MMORPG:
class Ability {
final name;
final effectDuration;
final recast; // wait time until next use
// ...
}
effectDuration and recast both carry the same type of information (i.e. duration of time) and are likely represented by the same datatype. It is easy to mix up which number goes where. However, they are both information vital to the correctness of the object, so they can't be missing during instantiation.
I could just break the program via a try-catch to enforce the requirement of those parameters, but that doesn't sound like fun for someone who uses the class and has no idea (short of reading the docs and understanding intuitively what the class does) that they are required.
Is there any way to enforce the requirement of certain named parameters while managing to inform the caller of said requirement and/or help them use it correctly?
The meta package provides a #required annotation that is supported by the DartAnalyzer.
Flutter uses this a lot and provides #required directly from import 'package:flutter/foundation.dart'
foo({#required String name}) {...}
foo(); // results in static warning
#required doesn't check if the passed value is null or not, only that a value was actually passed on the call site.
To check for null you can also use assert() to check for passed values
class Ability {
Ability(this.name, this.effectDuration, this.recast) : assert(name != null), assert(effectDuration != null), assert(recast != null);
final name;
final effectDuration;
final recast; // wait time until next use
// ...
}
[Update] New as-of Dart 2.0
In dart 2.0 the required keyword has been added to the language as part of the null-safety update. This means that you get a compiler-enforced non-null value rather than one checked by the analyzer; this makes the null check completely redundant.
This means that this code does effectively the same as the old code below, except that you never have to worry about the assertion throwing as the values for name, effectDuration, and recast cannot be null.
class Ability {
final String name;
final Duration effectDuration;
final bool recast;
final String? description;
Ability({
required this.name,
this.effectDuration = Duration(seconds: 1),
this.recast = false,
this.description,
});
}
Before Dart 2.0
Yes, there is!
Here's an example:
class Ability {
final String name;
final Duration effectDuration;
final bool recast;
final String description;
Ability({
#required this.name,
this.effectDuration = new Duration(seconds: 1),
this.recast = false,
this.description,
}):
assert(name != null),
assert(effectDuration != null);
}
You don't have to assert that name is not equal to null, but it might be useful for you.
Although you could use the flutter foundation package as described in the accepted answer, when I am working with model classes that don't need to know about Flutter, I prefer to use the meta package directly. That way it doesn't create an unnecessary dependency on the framework. This allows you to share the Dart code even outside of Flutter.
Add meta to pubspec.yaml:
dependencies:
meta: ^1.1.7
Import it in your class file:
import 'package:meta/meta.dart';
Use the #required annotation in your code:
class Person {
String name;
int age;
Person({#required this.name, this.age,});
}
So name is a required parameter, but age isn't.
final person = Person(name: 'Bob');
Update:
In an upcoming version of Dart, the required keyword should be added by default, so no imports will be necessary at all.
As of 2.12 with null safety you can use required keyword (not #required). Also no need to import any additional packages.
In this example named parameter name is optional while effectDuration and recast are required.
class Ability {
final name;
final effectDuration;
final recast;
Ability({this.name, required this.effectDuration, required this.recast});
}
Update pubspec.yaml, for example:
environment:
sdk: ">=2.12.0-0 <3.0.0"
References:
Sound null safety
How does #required compare to the new required keyword?
With null safety:
Non-nullable named parameter:
You need to either mark the named parameter required or provide a default value or even mark it late. For example:
class Foo {
final int a;
final int b;
late final int c; // Mark late and provide value later.
Foo({
required this.a, // Mark required.
this.b = 0, // Provided a default value.
});
}
Nullable named parameter:
You don't need anything special to handle them.
class Foo {
final int? z;
Foo({
this.z,
});
}
If you want declare to a empty variable but that has methods inside , you can:
1)Use the late keyword
2)Declare the type like possible null returned example: int? number;
3)Initialize the variable empty, for example :
List listOfNumbers = [];
Map mapOfPerson1 = {};
And so you can use the methods of the variable to add them values

Resources