Dart Error: Can't access 'this' in a field initializer - dart

I am having some trouble with class inheritance in dart and I'm not sure what I'm doing wrong. I am inheriting a class with the extends keyword, but when I try to make the constructor for that class I am getting some errors.
Here is my code:
#JsonSerializable(constructor: '_', fieldRename: FieldRename.snake)
class ParentClass {
final DateTime time;
final String bar;
ParentClass ._(
{required this.time,
required this.bar});
factory ParentClass.fromRecord(MyRecord record, String bar) {
return ParentClass._(
time: record.time.toUtc(),
bar: bar);
}
factory ParentClass.fromJson(Map<String, dynamic> json) =>
_$ParentClassFromJson(json);
Map<String, dynamic> toJson() => _$ParentClassToJson(this);
}
#JsonSerializable(constructor: '_', fieldRename: FieldRename.snake)
class ChildClass extends ParentClass {
final DateTime time;
final String bar;
final int id;
ChildClass._(
{required this.time,
required this.bar,
required this.id});
factory ChildClass.fromRecord(MyRecord2 record, String bar) {
return TimingEvent._(
id: record.id,
time: record.time.toUtc(),
bar: bar);
}
factory ChildClass.fromJson(Map<String, dynamic> json) =>
_$ChildClassFromJson(json);
Map<String, dynamic> toJson() => _$ChildClassToJson(this);
}
When I run this, I get the error: Error: The superclass, 'ParentClass', has no unnamed constructor that takes no arguments. This error is being encountered at the line where the ChildClass is constructed: ChildClass._(
I then try to resolve that error by specifying the constructor for the superclass within the child class. I do this by changing the code from this:
ChildClass._(
{required this.time,
required this.bar,
required this.id});
to this:
ChildClass._(
{required this.id})
: super._(time: this.time, bar: this.bar);
However, after making this change, I then get another error message: Error: Can't access 'this' in a field initializer.
How am I supposed to pass the values through to the superclass constructor without using this?

If you are using dart 2.17 or higher, the correct syntax for the constructor should be:
ChildClass._({required super.time, required super.bar, required this.id})
: super._();
Or if you using a version of dart lower than 2.17:
ChildClass._({required DateTime time, required String bar, required this.id})
: super._(time: time, bar: bar);
Note that you should also remove time and bar as properties from the ChildClass. These properties are already defined in the ParentClass, defining them again in ChildClass is redundant.

Related

Freezed class with generic type and fromJson, toJson

I'm trying to make a Freezed class with a generic type that has toJson and fromJson:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'option.freezed.dart';
part 'option.g.dart';
#Freezed(genericArgumentFactories: true)
class Option<TData> with _$Option<TData> {
const factory Option(
{required TData id,
required String name,
String? fontFamily,
required int iconCodePoint}) = _Option;
const factory Option.empty({
#Default(0) int iconCodePoint,
#Default(0 as TData) TData id,
#Default('') String name,
#Default(null) String? fontFamily,
}) = _Empty;
factory Option.fromJson(
Map<String, dynamic> json, TData Function(Object?) fromJsonTData) =>
_$OptionFromJson(json, fromJsonTData);
#override
Map<String, dynamic> toJson(Object? Function(TData) toJsonTData) {
return _$$_OptionToJson<TData>(this as _$_Option<TData>, toJsonTData);
}
Map<String, dynamic> _$$_OptionToJson<TData>(
_$_Option<TData> instance,
Object? Function(TData value) toJsonTData,
) =>
<String, dynamic>{
'id': toJsonTData(instance.id),
'name': instance.name,
'fontFamily': instance.fontFamily,
'iconCodePoint': instance.iconCodePoint,
'runtimeType': instance.$type,
};
}
The build runner executes the code generation without error, but when I try to run the app I get:
Error: The non-abstract class '_$_Option' is missing implementations
for these members: lib/…/option/option.freezed.dart:190
- Option._$$_OptionToJson
Try to either
- provide an implementation,
- inherit an implementation from a superclass or mixin,
- mark the class as abstract, or
- provide a 'noSuchMethod' implementation.
class _$_Option<TData> implements _Option<TData> {
^^^^^^^^^ : Context: 'Option._$$_OptionToJson' is defined here. lib/…/option/option.dart:30
Map<String, dynamic> _$$_OptionToJson<TData>(
^^^^^^^^^^^^^^^^ : Error: The non-abstract class '_$_Empty' is missing implementations for these members:
lib/…/option/option.freezed.dart:403
- Option._$$_OptionToJson
Try to either
- provide an implementation,
- inherit an implementation from a superclass or mixin,
- mark the class as abstract, or
- provide a 'noSuchMethod' implementation.
class _$_Empty<TData> implements _Empty<TData> {
^^^^^^^^ : Context: 'Option._$$_OptionToJson' is defined here. lib/…/option/option.dart:30
Map<String, dynamic> _$$_OptionToJson<TData>(
^^^^^^^^^^^^^^^^ : Error: Type variables can't be used in static members. lib/…/option/option.freezed.dart:406
this.id = 0 as TData,
^^^^^ : Error: Type variables can't be used as constants. lib/…/option/option.freezed.dart:406
this.id = 0 as TData,
^^^^^ : Error: Type variables can't be used as constants. lib/…/option/option.dart:16
#Default(0 as TData) TData id,
It looks like in the docs we don't need toJson but when I remove it I get this error in all the classes that contain a field of type Option
RangeError (index): Invalid value: Only valid value is 0: -1
when I run the build runner. What am I doing wrong?
Shouldn't the first constructor be
const factory Option.option(
?
I had a similar problem, see https://github.com/rrousselGit/freezed/issues/766

Create interface that contains Freezed class signatures so I can called freezed functions on my interfaces

I'm trying to have a base Freezed interface which my app entity interfaces can extend so I can call the freezed functions on the interfaces. I've started the process here which seems to be working so far:
abstract class IUserRegistrationEntity<T> extends FreezedClass<T> {
String get nickName;
String get email;
String get confirmEmail;
String get password;
String get confirmPassword;
}
abstract class FreezedClass<T> {
T get copyWith;
Map<String, dynamic> toJson();
}
freezed class:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:vepo/domain/user_registration/i_user_registration_entity.dart';
part 'user_registration_entity.freezed.dart';
part 'user_registration_entity.g.dart';
#freezed
abstract class UserRegistrationEntity with _$UserRegistrationEntity {
#Implements.fromString(
'IUserRegistrationEntity<\$UserRegistrationEntityCopyWith<IUserRegistrationEntity>>')
const factory UserRegistrationEntity(
{String nickName,
String email,
String confirmEmail,
String password,
String confirmPassword}) = _IUserRegistrationEntity;
factory UserRegistrationEntity.fromJson(Map<String, dynamic> json) =>
_$UserRegistrationEntityFromJson(json);
}
But now I need to add the fromJson factory constructor to the interface. I think this may be what I'm looking for although I can't really tell how to implement it in my code:
T deserialize<T extends JsonSerializable>(
String json,
T factory(Map<String, dynamic> data),
) {
return factory(jsonDecode(json) as Map<String, dynamic>);
}
You an then call it with:
var myValue = deserialize(jsonString, (x) => MyClass.fromJson(x));
Any help adding the fromJson to my freezed interface would be appreciated.
I've found a way to get the same benefits of programming to an interface or "abstraction" with freezed objects, while still getting to call those freezed functions:
#freezed
abstract class Person with _$Person {
#With(BasicPersonMixin)
const factory Person.basicPerson(
{int? id, String? firstName, String? lastName}) = BasicPerson;
#With(FancyPersonMixin)
const factory Person.fancyPerson({String? firstName, required String extraPropMiddleName, String? lastName}) =
FancyPerson;
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
const Person._();
void functionThatEveryPersonShares() {
print('I am a person');
}
String greet() {
return 'override me with a mixin or abstract class';
}
}
mixin FancyPersonMixin {
String get extraPropMiddleName {
return 'my default middle name is John`;
}
String greet() {
return 'Salutations!';
}
void specialisedFunctionThatOnlyIHave() {
print('My middle name is $extraPropMiddleName');
}
}
mixin BasicPersonMixin {
String greet() {
return 'Hi.';
}
}
Now we have 2 concrete classes: BasicPerson, and FancyPerson which are both a Person. Now we can program to Person throughout the app, and still call .copyWith and .fromJson and so on and so forth. The different types of Person can vary independently from each other by using mixins and still be used as a Person type. Works with generics etc (from docs - #With.fromString('AdministrativeArea<House>')) but I have kept the example simple for this question to most simply show the benefits. You can also make Person extend another base class.
I've found another way to let you be a bit more abstract than my other answer. Say you're in a highly abstract super-class, so you don't want to work with objects as specific as Person. You want to work with "a base freezed object"; just cast your type to dynamic in brackets and go ahead and use copyWith freely. Sure, it's not typesafe, but it's a worthy option if it allows you to do something in a super-class rather than in every sub-class.
mixin LocalSaveMixin<TEntity extends LocalSaveMixin<TEntity>> on Entity {
LocalRepository<TEntity> get $repository;
Ref? get provider;
TEntity $localFetch() {
return ($repository.$localFetch() as dynamic).copyWith(provider: provider)
as TEntity;
}
TEntity $localSave() {
return $repository.$localSave(entity: this as TEntity);
}
}

How can i pass an instance's constructor as a type to a function

I am trying to create a base class for my models but I am struggling with the error The name 'cls' isn't a type so it can't be used as a type argument.. So, how can I pass the object's constructor to the Hive.box method?
import 'package:hive/hive.dart';
class AppModel {
#HiveField(0)
int id;
#HiveField(1)
DateTime createdAt;
#HiveField(2)
DateTime updatedAt;
save() async {
final Type cls = this.runtimeType;
// The name 'cls' isn't a type so it can't be used as a type argument.
final Box box = await Hive.openBox<cls>(cls.toString());
await box.put(this.id, this);
return this;
}
}
#HiveType(typeId: 0)
class UserModel extends AppModel {
#HiveField(3)
String email;
#HiveField(4)
String displayName;
}
void main() {
final UserModel user = UserModel()
..email = 'user#domain.com'
..displayName = 'john doe';
user.save().then(() {
print('saved');
});
}
Dart does not have a way to refer to the dynamic type of this (a "self type").
The way such things are often handled is to have a self-type as type argument, so:
class AppModel<T extends AppModel> {
save() async {
final Box box = await Hive.openBox<T>(T.toString());
await box.put(this.id, this as T);
return this;
}
...
and then ensure that each subclass tells the superclass what type it is:
class UserModel extends AppModel<UserModel> {
...
}
(or, if you expect to subclass UserModel eventually:
class UserModel<T extends UserModel> extends AppModel<T> {
...
}
so that a subclass can still pass its type through).
You are also talking about constructors, and for that there is no easy solution.
Dart's type parameters are types, not classes. You cannot access static members or constructors from a type variable, and there is also no other way to pass a class around.
The only way you can have something call a constructor that it doesn't refer to statically, is to wrap the constructor call in a function and pass that function.
(I can't see how you need the constructor here).

Dart Abstract class of Generic Type with Named Constructor

I am attempting to construct an abstract class that requires a named constructor in Dart. Given some Map (m), this generic type must be able instantiate itself.
The Dart compiler is throwing T.fromJson -> Invalid constructor name.
My attempt at coding this:
abstract class JsonMap<T> {
Map toJson();
T.fromJson(Map m);
}
I struggled with the same concept (in the same place ... API parsing :)) ) and I didn't found a proper solution.
But maybe you can use something this thing I found while checking block pattern this (I am not using it for my model part):
abstract class SomeBase {
void load();
}
class Provider<T extends SomeBase> extends InheritedWidget {
final T something;
Provider({
Key key,
#required this.something,
}): super(key: key);
#override
bool updateShouldNotify(_) {
return true;
}
static Type _typeOf<T>() => T;
static T of<T extends SomeBase>(BuildContext context){
final type = _typeOf<Provider<T>>();
Provider<T> provider = context.inheritFromWidgetOfExactType(type);
return provider.something;
}
}
OR just use this without encapsulating it in an inherited widget and provide the already initialised objects (like user or whatever you are parsing) that just load the values from the JSON provided.
You're creating a class named JsonMap that is parameterized on type T. T is not the name of your class, so T.fromJson is not a valid named constructor for JsonMap.
If you want JsonMap to have a named constructor, it should be JsonMap.fromJson(Map m).
Untested, but off the top of my head, you should write your code like so:
abstract class JsonMap<T> {
Map<String, dynamic> toJson();
T fromJson(Map<String, dynamic> m);
}
The dot makes fromJson(Map m) a constructor of type T, or a static function belonging to type T. Without the dot, it is a function belonging to the abstract class JsonMap, returning type T. Specifying the map type is good practice if you know what it will be (like with json).

Constructors can't have type parameters.dart(type_parameter_on_constructor)

I am using the following dart packages (json_annotation, json_serializable, build_runner) to serialize/de-serialize json according to this page.
This is my code:
import 'package:json_annotation/json_annotation.dart';
part 'car_type.g.dart';
#JsonSerializable()
class CarType {
final int id;
#JsonKey(name: 'type_label')
final String label;
final String description;
CarType(this.id, this.label, this.description);
factory CarType.fromJson(Map<String, dynamic> json) =>
_$CarTypeFromJson(json);
factory List<CarType> CarType.fromJsonList(dynamic jsonArray){
final list = jsonArray as List;
final carTypesList = list.map((i) => CarType.fromJson(i));
return carTypesList;
}
}
So with the factory List<CarType> CarType.fromJsonList(dynamic jsonArray) I want to pass a json array to get back a list of CarType objects. However I'am getting a couple of compiler errors namely:
This function has a return type of 'List', but doesn't end with a return statement.dart(missing_return)
The default constructor is already defined.dart(duplicate_constructor_default)
Constructors can't have type parameters.dart(type_parameter_on_constructor)
Any idea what is going on?
factory List<CarType> CarType.fromJsonList(dynamic jsonArray){
You can't specify a return type for a constructor.
The return type is always the same as the class the constructor is member of.
Just replace factory with static and you should be fine,
except json_serializable expects a factory constructor, then you need to remove the return type and find another approach to get the List.

Resources