flutter lint argument type rules usage - flutter

How i can control witch rules i want to apply to my code.
I added lint package but argument type rules are happening to much.
I have a lot dynamic data from API calls and i tried disable theme but it didn't work.
Can i decide which rules i want to apply?
There are rules that make the code more efficient like the const rule, adding lint to active project can be headache so i think if it is worth it?
The current rules i try to disable:
argument_type_not_assignable: false
invalid_assignment: false
Article.fromMap throw The argument type 'dynamic' can't be assigned to the parameter type 'String'.dartargument_type_not_assignable
class Article {
String id;
String image;
String title;
Map contentEditor;
List<Map<String, dynamic>> teams;
List<Map<String, dynamic>> leagues;
String content;
String updateDate;
Article({
this.id,
this.image,
this.title,
this.contentEditor,
this.teams,
this.leagues,
this.content,
this.updateDate,
});
String getDateString() {
DateFormat formatter = DateFormat("MM/dd/yyyy");
return formatter.format(DateTime.parse(this.updateDate));
}
String getTimeString() {
final dateTime = DateTime.parse(this.updateDate).toLocal();
return DateFormat.Hm().format(dateTime);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'image': image,
'title': title,
'contentEditorId': contentEditor,
'teams': teams,
'leagues': leagues,
'content': content,
'updateDate': updateDate
};
}
factory Article.fromMap(Map<String, dynamic> map) {
if (map == null) return null;
return Article(
id: map['id'],
image: map['image'],
title: map['title'],
contentEditor: map['contentEditor'],
teams: List<Map<String, dynamic>>.from(map['teams']?.map((x) => x)),
leagues: List<Map<String, dynamic>>.from(map['leagues']?.map((x) => x)),
content: map['content'],
updateDate: map['updateDate'],
);
}
String toJson() => json.encode(toMap());
factory Article.fromJson(Map json) => Article.fromMap(json);
}
I like more this approach
dynamic getPoints(dynamic property) {
if (property == null) {
return 0;
}
return property.won * 3 + property.draw;
}
than this:
int getPoints(Map<String, int> property) {
if (property == null) {
return 0;
}
return property["won"] * 3 + property["draw"];
}

You can disable the strict type casting by following:
analyzer:
language:
strict-casts: false
strict-casts is a language mode of stricter type checks.
There are 3 modes.You can find more on this at Enabling stricter type checks

argument_type_not_assignable and invalid_assignment are errors, not lints.
You can make the Dart analyzer ignore them by modifying your analysis_options.yaml file to have:
analyzer:
errors:
argument_type_not_assignable: ignore
invalid_assignment: ignore
However, it does not make any sense to disable those rules. Even though the analyzer would no longer complain about violations, violations would still be illegal and ultimately would generate compilation errors. What would you expect int x = 'string'; to do?

Related

Why I am getting Instance members can't be accessed from a factory constructor?

I am getting following errors:
Instance members can't be accessed from a factory constructor. (Documentation) Try removing the reference to the instance member.
The argument type 'List<Map<String, dynamic>>?' can't be assigned to the parameter type 'List<Vaccination>'. (Documentation)
at line _convertVaccinations(json['vaccinations'] as List<dynamic>));
Code:
final String name;
final String? notes;
final String type;
final List<Vaccination> vaccination;
final String? referenceId;
Pet(this.name, {this.notes, required this.type, required this.vaccination, this.referenceId});
factory Pet.fromJson(Map<String, dynamic> json) =>
Pet(
json['name'] as String,
notes: json['notes'] as String,
type: json['types'] as String,
referenceId: json['referenceId'] as String,
vaccination:
_convertVaccinations(json['vaccinations'] as List<dynamic>));
List<Map<String, dynamic>>? _convertVaccinations(List<dynamic>? vaccinations) {
if (vaccinations == null) {
return null;
} else {
final vaccinationMap = <Map<String, dynamic>>[];
for (var element in vaccinations) {
vaccinationMap.add(element.toJson);
}
return vaccinationMap;
}
}
Factory and instance member error:
Well it is because factory Pet.fromJson(...) is a factory constructor, and the class method _convertVaccinations(...) is an instance member.
If you make _convertVaccinations(...) static, it will accept the use of it in the factory constructor.
Argument error:
vaccination is of type List<Vaccination> and the method _convertVaccination(...) returns either null or List<Map<String, dynamic>>
In other words, you cannot assign null to List<T> unless it says List<T>? and the class Vaccination is not a Map<String, dynamic>
Maybe you want to do something like final List<Vaccination>? vaccinations; OR return <Vaccination>[] instead of null if vaccinations == null.
So you'd probably want to do write _convertVaccinations(...) as:
static List<Vaccination>? _convertVaccination(List<dynamic>? vaccinations) {
return vaccinations?.map((e) => Vaccination.fromJson(e as Map<String,dynamic>).toList();
}
or:
static List<Vaccination> _convertVaccination(List<dynamic>? vaccinations) {
return vaccinations?.map((e) => Vaccination.fromJson(e as Map<String,dynamic>).toList() ?? <Vaccination>[];
}
Side note: Maybe you have more methods that you haven't presented here. Because it looks a bit wonky when your Pet.fromJson(...) use a element.toJson down the call stack.

Super class doesn't have a zero argument constructor

I am trying to use a base class in a data model.
I have a base class of Symptoms and I want to add Headache as an extension of Symptom
Right now this is my code
import 'package:cloud_firestore/cloud_firestore.dart';
class Symptom {
final String id;
final String path;
final DateTime startTime;
final String? type;
String get patientId => path.split('/')[1];
Symptom({
required this.id,
required this.path,
required this.startTime,
this.type,
});
factory Symptom.fromJson(
String id,
String path,
Map<String, Object?> doc,
) {
final start = doc['startTime'] as Timestamp;
return Symptom(
id: id,
path: path,
startTime: start.toDate(),
type: doc['type'] as String?,
);
}
Map<String, Object?> toJson() {
return {
'startTime': startTime,
'type': type,
};
}
}
class Headache extends Symptom {
int? intensity;
DateTime? endTime;
List<String> symptoms;
List<String> effects;
Map<String, int> medications;
bool? medsEffective;
String? notes;
Duration? get duration => endTime?.difference(startTime);
double get hours {
final inHours = duration?.inHours ?? 0;
final inMins = duration?.inMinutes ?? 0;
if (inHours < 1) {
return inMins / 60;
} else {
return inHours.toDouble();
}
}
Headache({
this.intensity,
this.medsEffective = false,
this.endTime,
this.notes,
this.symptoms = const [],
this.effects = const [],
this.medications = const {},
});
factory Headache.fromJson(
String id,
String path,
Map<String, Object?> doc,
) {
final start = doc['startTime'] as Timestamp;
final end = doc['endTime'] as Timestamp?;
final tempMeds = doc['medications'] as Map<String, dynamic>;
return Headache(
intensity: doc['intensity'] as int?,
notes: doc['notes'] as String?,
endTime: end?.toDate(),
medsEffective: (doc['medsEffective'] as bool?),
symptoms:
(doc['symptoms'] as List).map((item) => item as String).toList(),
effects: (doc['effects'] as List).map((item) => item as String).toList(),
// ignore: unnecessary_lambdas
medications: tempMeds.map((key, value) => MapEntry(key, value)),
);
}
Map<String, Object?> toJson() {
return {
'intensity': intensity,
'notes': notes,
'endTime': endTime,
'symptoms': symptoms,
'medsEffective': medsEffective,
'effects': effects,
'medications': medications,
};
}
}
When I try to do
Headache({
this.intensity,
this.medsEffective = false,
this.endTime,
this.notes,
this.symptoms = const [],
this.effects = const [],
this.medications = const {},
});
It gives me an error
The superclass 'Symptom' doesn't have a zero argument constructor.
Try declaring a zero argument constructor in 'Symptom', or explicitly invoking a different constructor in 'Symptom'
I am wondering how to fix this but also why is this error coming up and why does it need a zero argument constructor. Is extending a base class of a data model a good practice or should I shy away from this and make an entirely separate data model for headaches separate from symptoms?
If you don't explicitly call the super constructor in the constructor of child class, the compiler will try to implicitly call the default constructor of the super class (which, in this case, would be Symptom()).
Since you've defined a Symptom constructor that takes several arguments, there is no automatic default constructor for the class, so the Headache constructor is unable to initialize the fields of the super class.
You can resolve this by having your Headache constructor take additional arguments to initialize the super class:
Headache({
this.intensity,
this.medsEffective = false,
this.endTime,
this.notes,
this.symptoms = const [],
this.effects = const [],
this.medications = const {},
required String id,
required String path,
required DateTime startTime,
String? type,
}): super(
id: id,
path: path,
startTime: startTime,
type: type,
);
Refer to Michael's answer regarding the error you are receiving, but I want to comment on the how you are structuring your objects.
I would make a generic Illness class, with the name of the illness (e.g. "Headache") as a property so that you don't need to predefine every possible type of illness. Then I would suggest that Symptom should have a property field that holds an Illness object. If you want to constrain the types of illnesses, you can make the illness an enum that defines all possible illness types.
If you decide you have a good reason for directly creating classes for each specific illness, create an abstract Illness class and Headache should inherit from it so that all of the illnesses are interchangeable throughout the application.

Fauna DB and flutter, unable to get a default value on Select using the faunadb_http package

I am using the faunadb_http package and I want the value to be returned null from Fauna DB if the field does not exist in the collection. I am just not able to figure out what should I put in the default parameter of this package so that I get that back as the default value.
I tried the following two variations of default parameter and I get "Value not found at path" error for first and just an empty Object {} for second.
'itemPrice': Select(["data", "itemPrice"], Var("postDoc"), default_: null),
'itemLocation': Select(["data", "itemLocation"], Var("postDoc"), default_: Obj({})),
Can somebody help me understand what should I be passing to default_ so that I get a String or Int as a response back.
This is the code for the Select class from the package
#JsonSerializable()
class Select extends Expr {
#JsonKey(name: 'select')
final Object path;
final Expr from;
#JsonKey(name: 'default', disallowNullValue: true, includeIfNull: false)
final Expr? default_;
Select(this.path, this.from, {this.default_});
factory Select.fromJson(Map<String, dynamic> json) => _$SelectFromJson(json);
#override
Map<String, dynamic> toJson() => _$SelectToJson(this);
}
And this is for the Expr class
class Expr {
static Object? wrap_value(dynamic value) {
if (value is List) {
return wrap_values(value);
} else if (value is Map<String, dynamic>) {
return Obj(value);
} else if (value is DateTime) {
return Time(value.toUtc().toIso8601String());
} else {
return value;
}
}
static Object? wrap_values(Object? data) {
if (data == null) return null;
if (data is List) {
return List.generate(
data.length,
(e) => wrap_value(data[e]),
growable: false,
);
} else if (data is Map<String, dynamic>) {
return data.map(
(key, value) => MapEntry(key, wrap_value(value)),
);
}
return data;
}
Expr();
factory Expr.fromJson(Map<String, dynamic> json) => _$ExprFromJson(json);
Map<String, dynamic> toJson() => _$ExprToJson(this);
#override
String toString() {
return json.encode(this).toString();
}
}
I'm going to set aside the language-specific aspects, as I'm not familiar with Dart.
That said, as I read through your post it seems like Select() is working as defined. The third argument is what is returned if your data is not found, e.g., null.
In the first case, you are returning null explicitly, and Fauna removes keys with null values, so that value would indeed not be found.
In the second case, you are returning an empty Object, and you receive an empty Object, so that seems to be working as defined as well.
Can somebody help me understand what should I be passing to default_ so that I get a String or Int as a response back.
In this case you need to explicitly set an Expr that will evaluate to a string or Int. If the empty string "" and zero 0 are reasonable defaults, then you would want:
'itemPrice': Select(["data", "itemPrice"], Var("postDoc"), default_: 0),
and
'itemLocation': Select(["data", "itemLocation"], Var("postDoc"), default_: ""),
I got in touch with the author of the package and they were kind enough to fix the issue within a day of reporting it. Now it works as expected.

Acessing data from instance of a class

I have a question, I started learning dart/flutter, and when passing data from one screen to another I access the data like this
final orderData = ModalRoute.of(context).settings.arguments;
OrderData class looks like this
class OrderItem {
final String id;
final String name;
final String date;
final String address;
final String recordNumber;
OrderItem({
#required this.id,
#required this.name,
#required this.date,
#required this.address,
#required this.recordNumber,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'date': date,
'address': address,
'recordNumber': recordNumber,
};
}
factory OrderItem.fromMap(Map<String, dynamic> map) {
if (map == null) return null;
return OrderItem(
id: map['id'],
name: map['name'],
date: map['date'],
address: map['address'],
recordNumber: map['recordNumber'],
);
}
String toJson() => json.encode(toMap());
factory OrderItem.fromJson(String source) => OrderItem.fromMap(json.decode(source));
#override
String toString() {
return 'OrderItem(id: $id, name: $name, date: $date, address: $address, recordNumber: $recordNumber)';
}
#override
bool operator ==(Object o) {
if (identical(this, o)) return true;
return o is OrderItem &&
o.id == id &&
o.name == name &&
o.date == date &&
o.address == address &&
o.recordNumber == recordNumber;
}
#override
int get hashCode {
return id.hashCode ^
name.hashCode ^
date.hashCode ^
address.hashCode ^
recordNumber.hashCode;
}
}
My question is two fold(this is what vs code plugin generates for class). First is how can I access the data in the instance of the class(do I need for each specific getter), and second can somebody explain what toMap(), fromMap(), toJson(), fromJson(), bool operator ==(Object o), and hashCode getter do.
Looks like you're trying to use Flutter navigation methods.
Have a look at this blog post that explains how it works.
In summary, to "push" state to be used in the next screen, do something like this:
final arguments = OrderItem(
id = 'id',
name = 'name',
date = 'date',
address = 'address',
recordNumber = '10',
);
Navigator.pushNamed(context, NamedPagePassed.route, arguments: arguments);
You can later access it like this:
final OrderItem args = ModalRoute.of(context).settings.arguments;
print('The id of the order is ${args.id}');
You seem to have a whole lot of generated code in your class. The fromJson, toJson, fromMap and toMap methods are used for serialization (i.e. turn a Dart object into something that can be "transferred" to/from another language/network/etc).
The == operator and hashCode are used to check if an object instance is equal to another (which is very common in Flutter as Flutter wants to know if your UI state has been modified). hashCode allows a fast way to check that two objects are definitely not equal (you can know for sure that two objects are not equal if their hash-codes are different... if the hash-codes are equal, the objects may or may not be equal, but the probability they are NOT equal will be low because hash functions try to avoid "collisions", which is when two different objects have the same hash-code).
Hash-code and == are normally implemented together to give your class "identity". Just google around and you'll see how this all works.
I would recommend you don't use code generation from your IDE like this. Instead, get familiar with how Dart builders work, then use a codegen library that will automatically create these methods for you every time you compile (so changes to the data model are immediately reflected in the implementation of all these generated methods).
My recommendation is to use freezed for that.
Your code will be much more maintainable this way.

Add comma separated value to class list

I need to add the value in the list which is comma separated.
Sample data:
English,Hindi,French
Below is the class of List:
class LanguageService {
}
class Language extends Taggable {
final String name;
/// Creates Language
Language({
this.name,
// this.position,
});
#override
List<Object> get props => [name];
/// Converts the class to json string.
String toJson() => ''' {
"name": $name,\n
}''';
//}
String thuJson() => ''' {
"name": $name,
}''';
}
GetTags getTagsFromJson(String str) => GetTags.fromJson(json.decode(str));
class GetTags {
List<Content> content;
bool success;
//String error;
GetTags({
this.content,
this.success,
});
factory GetTags.fromJson(Map<String, dynamic> json) => GetTags(
content: (json["content"] as List).map((x) => Content.fromJson(x)).toList(),
success: json["success"],
);
}
class Content {
String tagname;
Content({
this.tagname,
});
factory Content.fromJson(Map<String, dynamic> json) => Content(
tagname: json == null ? 'Empty' : json["tagname"]
);
}
I tried split but it is giving me error.
List<Language> _selectedLanguages;
_selectedLanguages = [];
//responseBody['user_lang'] = 'English,Hindi,French' Data looks like this
_selectedLanguages = responseBody['user_lang'].split(', ');
Exception Caught: type 'List<String>' is not a subtype of type 'List<Language>'
Also tried.
_selectedLanguages.add(responseBody['user_lang']);
Exception Caught: type 'String' is not a subtype of type 'Language'
Update
I tried this too but getting error.
List _dbLanguages = responseBody['user_lang'].split(', ');
selectedLanguages = _dbLanguages.map<List<Language>>((item) => Language(item))
A value of type 'Iterable<List<Language>>' can't be assigned to a variable of type 'List<Language>'.
Try changing the type of the variable, or casting the right-hand type to 'List<Language>'.
One way you can do this is like this.
List<Language> _selectedLanguages;
_selectedLanguages = (responseBody['user_lang'].split(',') as List<String>).map((text) => Language(name: text)).toList();
Dart has a very good type checking system and I think your problem is an obvious one based on the error message. You must convert your list of String into a list of Language. I don't recall the syntax from the top of my head but I think you should be able to convert your list of String with .map<List<Language>>((item) => Language(item))