how to extend `Freezed` abstact classes - flutter

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?

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).

How can I inherit static methods in dart/flutter?

Is it possible in Dart/Flutter to inherit static methods or factories? Or do I need to workaround this by creating an instance to access that static method?
My case is that I want to serialize an object but need to access a general parse function for them.
abstract class Foo {
static Foo parse(); //Error, must have a body
Foo parse();//No error but need to call Foo().parse(); by creating an instance.
}
I want to create by using json so is bad practice and against performance to create a new instance to return another one?
class InheritedFoo {
final String string;
InheritedFoo(this.string);
#override
Foo parse() {
return InheritedFoo("some string");
}
}
Is it maybe possible to use a singleton to save performance (call InheritedFoo.inst.parse() )?
No you cannot do that. This excerpt is from the official Dart language specification:

Assigning subclass to a factory constructor in Dart

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)'.

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.

Class decorator, how to make sure the class is extending and implementing other classes

Sorry for the weird title, I don't quite know how to describe what I'm trying to do in one sentence.
I have to define a bunch of classes that are all going to extend from this one class and also implement this other class.
class SoulCoughing extends Super implements BonBon { /.../ }
class MoveAside extends Super implements BonBon { /.../ }
class LetTheManGoThru extends Super implements BonBon { /.../ }
I have written a sort of wrapper function that I use as a decorator for these classes.
const Eminem = function(klass: Constructable<????>) {
const instance = new klass();
// Do stuff
}
Constructable is a little interface I'm using because otherwise TypeScript would throw an error about not having a constructor.
interface Constructable<T> {
new(): T;
}
Now here is my problem, I don't know what type to assign to parameter klass in my wrapper function? I have tried doing this:
... function(klass: Contrusctable<Super & BonBon>)
and this:
... function(klass: Contrusctable<Super | BonBon>)
I also tried modifying my constructable interface like this:
interface Constructable<T, U> {
new(): T & U;
}
... function(klass: Contrusctable<Super, BonBon>)
but I keep getting an Argument of type 'typeof SoulCoughing' is not assignable to parameter of type 'Constructable<everythingIveTriedSoFar>' error.
So my question is, what type definition should I use with the parameter klass? I know I can just use any but I'd really like to make sure that the class being passed has extended Super and implemented BonBon.
I'm going to guess that the classes SoulCoughing etc. don't actually have no-arg constructors, and therefore cannot act as Constructable<{}> at all; the most likely culprit is that Super's constructor has a mandatory argument, which would make all subclasses fail to match new() by default. Note that this also implies that your implementation of Eminem probably wants to call new klass(...) with some arguments also.
The right way to fix it is to declare Constructable<T> to be a constructor with the right argument types. Let's say Super looks like this:
class Super {
constructor(elevator: number, mezzanine: string) {
//...
}
}
Then you could define Constructable to match:
interface Constructable<T extends Super & BonBon = Super & BonBon> {
new(chump: number, change: string): T; // same args as Super
}
and Eminem like:
const Eminem = function(klass: Constructable) {
const instance = new klass(2, "rise");
// Do stuff
}
and finally:
Eminem(SoulCoughing); // no error
I only kept Constructable generic in case you wanted TypeScript to preserve the type of the particular subclass, like so:
const SlimShady = function <T extends Super & BonBon>(klass: Constructable<T>): T {
return new klass(2, "fat");
}
// returns same type as passed-in constructor
const cutLean: MoveAside = SlimShady(MoveAside);
Okay, hope that helps; good luck!