How to set up an interface with an abstract member variable in Dart - flutter

I'm building a Flutter application and wanted to define a common scheme for all my pages. All pages should have a static id variable that I use for routing with named routes. (instead of having to instanciate MainMenuPage().id for Navigator.pushNamed(..., MainMenuPage ().id), I prefer to use the MainMenu.id.
In the MainMenuPage page file, this currently looks like:
class MainMenuPage extends StatelessWidget {
const MainMenu();
static String id = 'main-menu-id'; // added by me, but not "enforced" through dart logic
}
I am aware that the flutter-way of Widget-Stacking is composition over inheritance. Still, I was wondering why I cannot create a superclass of all my pages to force them to implement this static variable.
In my imagination this would be looking like this:
abstract class MyAppPage() {
static String id;
}
class NotAbstractPage() extends StatelessWidget implements MyAppPage {
static String id = 'foobar'; // this line is enforced through the quasi-interface
}
Unfortunately, this approach currently does not work in Dart. How could one make this work with some interfacy-ish mechaning?

You can force your pages to implement the variable in your superclass but it shouldn't be a static variable like this :
abstract class MyAppPage {
late String id ;
}
if you Implement this class without overriding this variable it will give you an error :
after overriding it:

Related

Dart - Way to access a inherited static property from a parent class method

In PHP there is a way of accessing a static property value that is defined/overridden on an inheritor.
e.g.
class Foo {
public static $name='Foo';
public function who(){
echo static::$name;//the static operator
}
}
class Bar extends Foo {
public static $name='Bar';
}
$bar = new Bar();
$bar->who();
//Prints "Bar";
Is there ANY way of doing the exact same thing in Dart language?
Addressing comments:
About making it instance prop/method: There's a reason for the existence of static properties and methods and it's not having to create a new instance of the object to access a value or functionality that is not mutable.
Yes, but that's not how you are using it. Your use case is to invoke the method on an object, and therefore you really want an instance method. Now, some languages automatically allow invoking class methods as instance methods, and I see two choices for a language that offers that ability:
Statically transform fooInstance.classMethod() to ClassFoo.classMethod() based on the declared type (not the runtime type) of the object. This is what Java and C++ do.
Implicitly generate virtual instance methods that call the class method. This would allow fooInstance.classMethod() to invoke the appropriate method based on the runtime type of the object. For example, given:
class Foo {
static void f() => print('Foo.f');
}
You instead could write:
class Foo {
static void classMethod() => print('Foo.f');
final instanceMethod = classMethod;
}
and then you either could call Foo.classMethod() or Foo().instanceMethod() and do the same thing.
In either case, it's syntactic sugar and therefore isn't anything that you couldn't do yourself by being more verbose.
About the "meaning of static" and "only work because they allow invoking class methods as instance methods" : That affirmation is actually wrong. In the case of PHP, as per the example above, the Language is providing a way to access the TYPE of the class calling the method in the inheritance chain. A(methodA) >B > C. When C calls methodA, PHP allows you to know that the class type you're in is indeed C, but there's no object instance attached to it. the word "static" there is a replacement for the caller class type itself
All of that is still known at compilation time. That C derives from B derives from A is statically known, so when you try to invoke C.methodA, the compiler knows that it needs to look for methodA in B and then in A. There's no dynamic dispatch that occurs at runtime; that is still compile-time syntactic sugar. That is, if you wanted, you could explicitly write:
class A {
static void methodA() {}
}
class B extends A {
static void methodA() => A.methodA();
}
class C extends B {
static void methodA() => B.methodA();
}
Anyway, in your example, you could write:
class Foo {
static String name = 'Foo';
String get nameFromInstance => name;
void who() {
print(nameFromInstance);
}
}
class Bar extends Foo {
static String name = 'Bar';
#override
String get nameFromInstance => name;
}
void main() {
var bar = Bar();
bar.who(); // Prints: Bar
}

Flutter plugin instance with parameters

I am trying to make a plugin for authentication. It will act like a "wrapper" (but with additional functionality for each plugin) for different already existing packages for different platforms.
By the additional functionality I mean
Web implementation already has state management
Android implementation is AppAuth, so here I need to add my own state management
I am trying to use the PlatformInterface way, but I cannot find any guide how to make it instantiable with constructor params.
Let's say I have this code:
abstract class KeycloakAuth extends PlatformInterface {
KeycloakAuth()
: super(token: _token);
static final Object _token = Object();
static late KeycloakAuth _instance = KeycloakAuth._setPlatform();
static KeycloakAuth get instance => _instance;
static set platform(KeycloakAuth instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
factory KeycloakAuth._setPlatform() {
if (Platform.isAndroid) {
return KeycloakAuthAppAuth();
} else {
throw UnimplementedError('The current platform ${Platform.operatingSystem} is not supported.');
}
}
}
This would work, but in my app I would like to do this:
final _keycloakAuth = KeycloakAuth(keycloakUrl: 'xxx', clientId: 'yyy');
If I simply tried to add params like this:
abstract class KeycloakAuth extends PlatformInterface {
final String keycloakUrl;
final String clientId;
KeycloakAuth({
required this.keycloakUrl,
required this.clientId,
})
: super(token: _token);
...
I wouldn't be able to pass them inside the _setPlatform() method since this is static method.
Also KeycloakAuthAppAuth extends KeycloakAuth, so I would need to pass those parameters back and forth, like instantiate KeycloakAuth, this instantiates KeycloakAuthAppAuth which needs to pass those parameters back via super()...
Any tips for this?
I know I can make a method initialize({String keycloakUrl, String clientId}) with my parameters, but I would still like to know if it's possible to make it using the constructor.

How can I add a custom function inside the class annotated with the 'freezed' library?

I have defined a class like this and annotated with the freezed library.
#freezed
#immutable
abstract class CommentMediaAttachmentModel with _$CommentMediaAttachmentModel {
const factory CommentMediaAttachmentModel({
final String type,
final String mediaUrl,
final int width,
final int height
}) = _CommentMediaAttachmentModel;
bool isAnimated() {
return type == 'ANIMATED';
}
}
I'd like to add a quick function isAnimated to determine the type variable, but on compilation, it doesn't allow me to do so:
lib/presentation/comment/model/comment_attachment_model.freezed.dart:292:7: Error: The non-abstract class '_$_CommentMediaAttachmentModel' is missing implementations for these members:
- CommentMediaAttachmentModel.isAnimated
Try to either
- provide an implementation,
- inherit an implementation from a superclass or mixin,
- mark the class as abstract, or
- provide a 'noSuchMethod' implementation.
Upon checking the generated class _$_CommentMediaAttachmentModel, isAnimated function isn't implemented. How can I achieve that?
Edit: Below is the code of _$_CommentMediaAttachmentModel.
I'm not sure why I cannot paste that snippet to SO, it just said the code is malformed. I will use a screen capture instead:
To manually define methods/properties on the class, as stated in freezed documentation, you have to define a single private constructor:
#freezed
#immutable
abstract class CommentMediaAttachmentModel with _$CommentMediaAttachmentModel {
const CommentMediaAttachmentModel._(); // Added constructor
const factory CommentMediaAttachmentModel({
final String type,
final String mediaUrl,
final int width,
final int height
}) = _CommentMediaAttachmentModel;
bool isAnimated() {
return type == 'ANIMATED';
}
}
Basically You need to implement isAnimated inside the _$CommentMediaAttachmentModel too. because it is a mixin of the abstract class.

In Dart I'm trying to extend a class while changing the TYPE of one of its properties

Class1 has a property List<ClassA> xyz = [];. ClassA has a number of properties and methods. ClassB extends ClassA adding additional properties and methods. Is there a way for me to create Class2 extending Class1 but change the TYPE of xyz to List<ClassB>? If that is confusing hopefully code below will give an example of what I'm trying to accomplish. Basically the same as overriding a method but overriding a property.
class Game {
int points;
String opponent;
int opponentPoints;
}
class FootballGame extends Game {
int touchdowns;
int opponentstouchdowns;
}
class BaseballGame extends Game {
int homeruns;
int opponentsHomeruns;
}
class Team {
String name;
List<Game> games;
double winningPercentage() {
int wins = 0;
for(var game in games){
wins += (game.points > game.opponentPoints) ? 1 : 0;
}
return wins / games.length;
}
}
class FootballTeam extends Team {
// How do I change the TYPE of the games property to <FootballGame>
}
You could use the covariant keyword in this case:
class FootballTeam extends Team {
#override
covariant List<FootballGame> games;
}
However, be aware that doing so is potentially unsafe; the reason why you need the covariant keyword is to suppress the type error that arises because the override can violate the contract of the base class: the base class advertises that games can be assigned a List<Game>, but such an assignment would be invalid for the derived class. By using the covariant keyword, you disable the type-check and take responsibility for ensuring that you do not violate the contract in practice.
Note that if the games member were final (or were only a getter), then the override (which uses a more specific type) would be safe and wouldn't need to use covariant.
Edit
I had forgotten that I had written this answer when writing a more detailed answer for a similar question.

Typescript access static attribute of generic type

I have an abstract class Model with a static attribute and another generic class Controller<T extends Model>. I want to access the static attribute of Model in an instance of Controller. That should like this:
abstract class Model{
static hasStatus: boolean = false;
}
class MyModel extends Model{
static hasStatus = true;
}
class Controller<T extends Model>{
constructor(){
if(T.hasStatus)...
}
}
But TS says 'T' only refers to a type, but is being used as a value here.
Is there an easy way to achieve this? Or should i subclass Controller for each Heritage of Model and implement a method to retrieve the value?
There is no way to do that in typescript. Generic type parameters can only appear where types may appear in declarations, they are not accessible at runtime. The reason for that is simple - single javascript function is generated for each method of the generic class, and there is no way for that function to know which actual type was passed as generic type parameter.
If you need that information at runtime, you have to add a parameter to the constructor and pass a type yourself when calling it:
class Controller<T extends Model>{
constructor(cls: typeof Model){
if (cls.hasStatus) {
}
}
}
let c = new Controller<MyModel>(MyModel);
Here is how it looks when compiled to javascript to illustrate the point - there is nothing left of generic parameters there, and if you remove cls parameter there is no information about where hasStatus should come from.
var Controller = (function () {
function Controller(cls) {
if (cls.hasStatus) {
}
}
return Controller;
}());
var c = new Controller(MyModel);