Assigning subclass to a factory constructor in Dart - flutter

The Key class in Flutter has a factory constructor which works more like a variable.
abstract class Key {
const factory Key(String value) = ValueKey<String>;
// ...
}
But when I do something like that, I get an error:
class Foo {
Foo.empty();
const factory Foo(int i) = Bar; // Error
}
class Bar extends Foo {
Bar() : super.empty();
}
Actually I didn't quite get what is the use of this factory constructor cum variable. Can anyone please explain.

A constructor like:
const factory Key(String value) = ValueKey<String>;
is called a redirecting factory constructor. They're not well-known (even within the Dart and Flutter teams) since they aren't mentioned in the Dart Language Tour, but they're mentioned in the Dart Language Specification in (as of version 2.10) section 10.6.2:
A redirecting factory constructor specifies a call to a constructor of another
class that is to be used whenever the redirecting constructor is called.
Your attempt to use them:
const factory Foo(int i) = Bar; // Error
doesn't work for two reasons:
You declared the Foo factory constructor as const, but the default Bar constructor is not const. Either remove const from the Foo factory constructor or make the Bar default constructor const (which also would require making the Foo.empty constructor const).
Note that when you use a redirecting factory constructor with =, there's no opportunity for you to specify how to pass arguments. That's because a redirecting factory constructor requires that both constructors have the same parameters. Either remove the unused parameter from the Foo factory constructor or make Bar's constructor take an int argument too.
You should pay attention to the errors that you get from static analysis; they explain the above two issues. In DartPad, I get:
A constant redirecting constructor can't redirect to a non-constant constructor.
and
The redirected constructor 'Bar Function()' has incompatible parameters with 'Foo Function(int)'.

Related

How to stub getter or a method of freezed dataclass in Flutter / Dart

I have a freezed data class with some fields. A getter method returns nested elements of one attribute for easier access.
#freezed
class Airport with _$Airport {
const Airport._();
const factory Airport({
required String identifier
required String type,
required List<Runway> runways,
}) = _Airport;
List<Ils> get allIls => runways
.map((runway) => runway.allIls)
.expand((ils) => ils)
.toList();
}
I use the Airport class inside a test where the getter allIls is called. I don't want to fill runways with legit data, instead I directly want to stub the getter method allIls and have it return a list of objects.
What I tried:
Mock the Airport class:
class MockAirport extends Mock implements Airport {}
My test:
test('',
() async {
final airport = MockAirport();
final ilsList = [ils1, il2];
when(airport.allIls).thenReturn(ilsList);
expect(...);
});
However, this gives me the following error:
type 'Null' is not a subtype of type 'List<Ils>'MockAirport.allIls
I have also tried a "normal" method instead of a getter, with the same results:
List<Ils> allIls2() => runways
.map((runway) => runway.allIls)
.expand((ils) => ils)
.toList();
...
when(airport.allIls2.call()).thenReturn(ilsList);
Any idea what I could do?
It looks like you missed a step in setting up your Mocks.
You need to add the GenerateMocks or GenerateNiceMocks attribute somewhere in your library to auto-generate your Airport mocks. I like to have the attribute in the same file as my test, though that can lead to repeated mocks throughout your test files. After you have the attribute, you generate the mock using build_runner.
Lastly, in your second example with the "normal" method, you don't need to add the .call() to your when statement. In fact, adding that will cause the call to fail. It should instead just be when(airport.allIls2()).thenReturn(ilsList).

The superclass 'BluetoothDevice ' doesn't have a zero argument constructor

I am new to flutter and i want to use this code from a youtube tutorial
class _DeviceWithAvailability extends BluetoothDevice {
BluetoothDevice device;
_DeviceAvailability availability;
int? rssi;
_DeviceWithAvailability(this.device, this.availability, [this.rssi]);
}
but it giving an error
The superclass 'BluetoothDevice ' doesn't have a zero argument constructor.
and suggestion
Try declaring a zero argument constructor in 'BluetoothDevice ', or
explicitly invoking a different constructor in 'BluetoothDevice '.
I read the documentation but it isn't clear to me.
Please help mo to solve this.
The problem is in your BluetoothDevice device initialization, because the constructor need many parameters that are mandatory, you have two solutions.
1.Initializate this variable with empty parameters like this:
BluetoothDevice device = BluetoothDevice(...);
2.Add late property and before use this var do you need to initialize with correct information, like this:
late BluetoothDevice device;
and then, before use this var you need to pass the correct information like this:
device = BluetoothDevice(...);
Your suggestion means,
A zero-argument constructor is literally a constructor that can be
invoked with zero arguments. This includes constructors that take only
optional arguments.
Some examples:
SomeClass();//this is zero argument constructor
SomeClass2(String argument1);//this is one argument constructor
SomeClass3(String argument1,String argument2);//this is 2 argument constructor

How to declare final class in Dart to prevent extending from it?

In Java\Kotlin we have a String class that is final and immutable.
I tried to mark the class with final keyword but looks like it's not allowable.
So, I'm a little bit confusing, how to declare final class in Dart ?
Note: the case is - I want to instantiate this class outside, but forbid to extending it. So using the private constructor - it's not my case.
You can achieve this final effect from java by having a private constructor for your class, it will prevent the class from being extended, BUT it will also prevent the class from being instantiated (only in the same file both will be possible):
class MyString {
MyString._(); // use _ for private constructor.
static void print(String s) {
print(s);
}
}
Call with
String message = "Hello World";
MyString.print(message);
Dart considers that we are all adults, preventing class extension is hence part of the design and responsability of the developers to have clear class names, and not part of the language:
AVOID extending a class that isn’t intended to be subclassed.
If a constructor is changed from a generative constructor to a factory constructor, any subclass constructor calling that constructor will break. Also, if a class changes which of its own methods it invokes on this, that may break subclasses that override those methods and expect them to be called at certain points.
Difference of meaning for final with Java
Dart has a very simple definition of what is final: a variable in dart can only be set once, id est: is immutable.
Final and const
If you never intend to change a variable, use final or const, either instead of var or in addition to a type.
A final variable can be set only once; a const variable is a compile-time constant. (Const variables are implicitly final.) A final top-level or class variable is initialized the first time it’s used.
Additionally to the approach of making the constructor private and instantiating your object via a static factory, you could use the package meta and
annotate your final class as sealed:
#sealed
class Z{}
This will signal users of your package that this class should not be extended or implemented. For example in vscode trying to extend the class Z:
class Z1 extends Z{}
results in the following warning:
The class 'Z' shouldn't be extended, mixed in,
or implemented because it is sealed.
Try composing instead of inheriting, or refer
to its documentation for more information.dart(subtype_of_sealed_class)
The issue will also be picked up by the dart analyzer:
$ dart analyze
Analyzing test... 0.8s
info • lib/src/test_base.dart:3:1 •
The class 'Z' shouldn't be extended, mixed in, or implemented because it
is sealed. Try composing instead of inheriting, or refer to its
documentation for more information. • subtype_of_sealed_class
You can use the factory unnamed constructor along with private named constructor, like this:
class NonExtendable {
NonExtendable._singleGenerativeConstructor();
// NonExtendable();
factory NonExtendable() {
return NonExtendable._singleGenerativeConstructor();
}
#override
String toString(){
return '$runtimeType is like final';
}
}
In a client code, in the same library, or another library, an instance can be created, an example:
// Create an instance of NonExtendable
print ('${NonExtendable()}');
Trying to extend it, something like
class ExtendsNonExtendableInSameLibrary extends NonExtendable {
ExtendsNonExtendableInSameLibrary._singleGenerativeConstructor() : super._singleGenerativeConstructor();
factory ExtendsNonExtendableInSameLibrary() {
return ExtendsNonExtendableInSameLibrary._singleGenerativeConstructor();
}
}
will work in the same library (same 'source file') but not in another library, making the class NonExtendable same as 'final' in Java from the perspective of any client code.

how to extend `Freezed` abstact classes

update: edited as my original question is answered in this issue,
given an freezed class cannot directly implement other classes (see issue above)
Foo.baz can implement Baz as below
abstract class Baz<T> {
const Baz(this.data);
final T data;
}
#freezed
abstract class Foo<T> with _$Foo<T> {
#Implements(Baz)
const factory Foo.baz(T data) = _FooBaz;
}
but the Type T doesn't get passed to the implemented Baz in the
abstract class _FooBaz<T> implements Foo<T>, Baz<dynamic> { /// `<= HERE!!!!`
const factory _FooBaz(T data) = _$_FooBaz<T>;
T get data;
_$FooBazCopyWith<T, _FooBaz<T>> get copyWith;
}
if I try to pass the the Type inside #Implements
I get the error
Arguments of a constant creation must be constant expressions. Try making the argument a valid constant, or use 'new' to call the constructor.
is there any way to correctly pass the type to the implemented class?

Flutter required keyword

I don't really understand how required works. For example I've seen this code:
class Test{
final String x;
Test({
required this.x
});
factory Test.initial(){
return Test(x: "");
}
}
But what should required do here? Seems like it makes an optional parameter a non optional parameter.
Update
As of Dart 2.12, the required keyword replaces the #required meta annotation. For detailed info look into the official FAQ. The following answer has been updated to reflect both this and null safety.
Parameters required by default
The parameters of a class constructor or function are required by default.
class Test {
final String x;
Test(this.x);
}
You're not allowed to do this:
final value = Test();
// 1 positional argument(s) expected, but 0 found.
You must do this:
final value = Test('hello');
Optional named parameters
If you surround a parameter with curly braces, though, in addition to becoming a named parameter, it also becomes optional.
Since it's optional, the property must either be nullable like this:
class Test {
final String? x;
Test({this.x});
}
Or it has to have a default value like this:
class Test {
final String? x;
Test({this.x = ''});
}
So now this is ok:
final value = Test();
And so is this:
final value = Test(x: 'hello');
Required named parameters
Sometimes you don't want to allow a parameter to be null and there is no natural default variable. In that case you can add the required keyword in front of the parameter name:
class Test {
final String x;
Test({required this.x});
}
This is not ok anymore:
final value = Test();
// The named parameter 'x' is required, but there's no corresponding argument.
But this is still fine:
final value = Test(x: 'hello');
Dart 2.12 (null safety):
Beginning with Dart 2.12, the #required annotation is now replaced by the required keyword. You should mark your field required if it is mandatory for others to pass some value to it.
For example:
class Foo {
final int a; // Mandatory? Use 'required'
final int b; // Not mandatory? Don't use 'required'
Foo({
required this.a, // Marked 'required'
this.b = 1,
});
}
Usage:
Foo(); // Error: 'a' is required
Foo(a: 0); // Good
Foo(a: 0, b: 1); // Good
#required is an annotation that will create a warning for you to remember that the named parameter is necessary for the class to work as expected.
It will not create compile errors, at least for what I know.
#required bounds you to pass #required marked arguments while creating object of Class. For example, while showing a dialog, you'd mark context as required since, you cannot show dialog without having a valid context. But, you should not overuse it.
Short answer: Named parameters are optional by default in Dart. We prefer them to positional params for ease of use. In this case, the named parameters also might be expected to hold some value all the time (non-nullable) - from initialization itself. Hence, the double effort.
He could use default value initialization of the parameters instead of 'required', if the values were compile-time constants, and that doesn't seem to be the case here.
Positional parameters can be required or optional, which we pass in order when calling. The following is an example of required positional parameters' usage:
class Object{
String name;
int value;
Object(this.name, this.value=100); //auto type inference
}
final one = Object("Name here", 50); // All parameters are needed to call.
Named parameters are another type of optional parameters. Flutter APIs use named parameters and in our UI code, it is preferred to use named parameters instead of positional parameters. Reason being readability and clarity when reading code or calling the constructors later on at several parts of the code. You would have seen this as the case with all Widgets, Styles. For if it were to be positional it would be difficult to keep track of them upon calling with the sheer amount of methods that would be in use, and dynamic type inference could also be at work.
void display({required String name, int value1, int value2=100}) {...;} //named params
display(value1: 50, name: "Calculated name");
NOTE:
If exists, required positional parameters have to come first. Either named or optional positional params can follow(NOT BOTH).
String say(String from, String msg, [String? device]) { //req. pos params and opt pos params.
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
Remove
required
in constructor.
Instead write
final String? x;
So, it becomes as:
class Test{
final String? x;
Test({
this.x
});
factory Test.initial(){
return Test(x: "");
}
}