Using comparable on #freezed annotated class is causing compile time error - flutter

I have to sort items of a class created with #freezed annotation. I have added Comparable interface to the signature. It is causing following compile time exception:
Error (Xcode): lib/models/app_language.freezed.dart:143:7: Error: The
non-abstract class '_$_AppLanguage' is missing implementations for
these members:
Could not build the application for the simulator.
// app_language.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'app_language.freezed.dart';
part 'app_language.g.dart';
#freezed
class AppLanguage with _$AppLanguage implements Comparable<AppLanguage> {
const factory AppLanguage({
#Default("en") String id,
String? name,
String? msg,
#Default([0, 0, 0, 0]) List<int> color,
#Default(false) bool rtl,
}) = _AppLanguage;
factory AppLanguage.fromJson(Map<String, dynamic> json) =>
_$AppLanguageFromJson(json);
#override
int compareTo(AppLanguage other) {
if (rtl == other.rtl) {
if (name != null && other.name != null) {
return name!.compareTo(other.name!);
} else if (name == null && other.name == null) {
return 0;
} else if (name != null) {
return 1;
} else {
return -1;
}
} else {
return rtl ? 1 : -1;
}
}
}
pubspec.yaml (snippet)
dependencies:
freezed: ^2.3.2
freezed_annotation: ^2.2.0
dev_dependencies:
build_runner: ^2.3.3
json_serializable: ^6.6.0
usage
// here value is list of AppLanguages and I am returning another sorted list.
return <AppLanguage>[...value]..sort();
How do I resolve compilation error? Any help will be appreciated.
Right now,
I have added separate comparator so that it could work. But having the model class implements the Comparable is desired.
import 'package:freezed_annotation/freezed_annotation.dart';
import '../localization/localization.dart';
part 'app_language.freezed.dart';
part 'app_language.g.dart';
#freezed
class AppLanguage with _$AppLanguage {
const factory AppLanguage({
#Default(localeDefault) String id,
String? name,
String? msg,
#Default([0, 0, 0, 0]) List<int> color,
#Default(false) bool rtl,
}) = _AppLanguage;
factory AppLanguage.fromJson(Map<String, dynamic> json) =>
_$AppLanguageFromJson(json);
// #override
// // ignore: library_private_types_in_public_api
// int compareTo(_$_AppLanguage other) {
// if (rtl == other.rtl) {
// if (name != null && other.name != null) {
// return name!.compareTo(other.name!);
// } else if (name == null && other.name == null) {
// return 0;
// } else if (name != null) {
// return 1;
// } else {
// return -1;
// }
// } else {
// return rtl ? 1 : -1;
// }
// }
}
int appLanguageComparator(AppLanguage a, AppLanguage b) {
if (a.rtl == b.rtl) {
final aName = a.name;
final bName = b.name;
if (aName != null && bName != null) {
return aName.compareTo(bName);
} else if (aName == null && bName == null) {
return 0;
} else if (aName != null) {
return 1;
} else {
return -1;
}
} else {
return a.rtl ? 1 : -1;
}
}
Usage:
return <AppLanguage>[...value]..sort(appLanguageComparator);

For others that will encounter the same issue in the future, in order to add custom methods/getters etc to a freezed class you need to provide a empty const constructor. For example:
#freezed
class Person with _$Person {
const factory Person(String name, {int? age}) = _Person;
void method() {
print('hello world');
}
}
This implementation will throw a compile error and in order to make it work you need to do:
#freezed
class Person with _$Person {
// Added constructor. Must not have any parameter
const Person._();
const factory Person(String name, {int? age}) = _Person;
void method() {
print('hello world');
}
}
Keep in mind, the constructor must be an empty constructor.

You have to extend the class with Comparable<AppLanguage> and should override compareTo with parameter of AppLanguage as following
import 'package:freezed_annotation/freezed_annotation.dart';
import '../localization/localization.dart';
part 'app_language.freezed.dart';
part 'app_language.g.dart';
#freezed
class AppLanguage extends Comparable<AppLanguage> with _$AppLanguage {
const factory AppLanguage({
#Default(localeDefault) String id,
String? name,
String? msg,
#Default([0, 0, 0, 0]) List<int> color,
#Default(false) bool rtl,
}) = _AppLanguage;
factory AppLanguage.fromJson(Map<String, dynamic> json) =>
_$AppLanguageFromJson(json);
#override
int compareTo(AppLanguage other) {
if (rtl == other.rtl) {
if (name != null && other.name != null) {
return name!.compareTo(other.name!);
} else if (name == null && other.name == null) {
return 0;
} else if (name != null) {
return 1;
} else {
return -1;
}
} else {
return rtl ? 1 : -1;
}
}
}

Related

Don't execute assignment if value is null

I am still coming up to speed with dart and wanted to know if there was an easier way to not execute a statement if the value is null. See example below:
I can always do the if statements below for setting field3 and field4, but felt like something like field5 should work. But when I try to do that, it complains that a null check operator is used on a null value.
Also I don't want to change the Map to have a dynamic value.
Is there a single one liner to do what I am trying to do, or do I just need to check for null before setting the value.
Map<String, Object> myMap = {};
print('running now');
try {
myMap['field1'] = DummyClass.getString('hello');
myMap['field2'] = DummyClass.getString('good');
//Is there a more concise way to do this than the 2 options below?
if (DummyClass.getOptionalString('goodbye') != null) {
myMap['field3'] = DummyClass.getOptionalString('goodbye')!;
}
String? temp = DummyClass.getOptionalString('go');
if (temp != null) {
myMap['field4'] = temp;
}
// This gives an error 'null check operator used on a null value'
// myMap['field5'] ??= DummyClass.getOptionalString('to')!;
} catch (e) {
print('error condition, $e');
}
print(myMap);
}
class DummyClass {
static String getString(String? strParam) {
String? retString = getOptionalString(strParam);
if (retString == null) {
throw ('nulls are not allowed');
}
return retString;
}
static String? getOptionalString(String? strParam) {
if (strParam == null || strParam.length < 3) {
return null;
}
return strParam;
}
}
There's no built-in way to do what you want, but you could write a function (or extension method) to do it. For example:
extension MapTrySet<K, V> on Map<K, V> {
void trySet(K key, V? value) {
if (value != null) {
this[key] = value;
}
}
}
and then you could do:
myMap.trySet('field3', DummyClass.getOptionalString('goodbye'));
myMap.trySet('field4', DummyClass.getOptionalString('go'));
Alternatively, if you really want to use normal Map syntax, you could create your own Map class that has a void operator []=(K key, V? value) override and does nothing when the value is null, but that probably would not be worth the effort.
The issue is that the ??= operator assigns to the left if it is null. Expanded, it would look something like this:
a ??= b;
// Equivalent to:
if (a == null) {
a = b;
}
Which is not something that you're trying to achieve. AFAIK, there is no such operator yet in Dart. However, you can try this:
final possiblyNullValue = '';
final myMap = <String, String>{};
myMap['key'] = possiblyNullValue ?? myMap['key'];
// Equivalent to:
if (possiblyNullValue != null) {
myMap['key'] = possiblyNullValue;
}
// or:
myMap['key'] = possiblyNullValue != null? possiblyNullValue : myMap['key'];
Which would work in your case as a one-liner.
You could create your map with all entries, even null, and then filter the null values out:
void main() {
try {
final myMap = <String, dynamic>{
'field1': DummyClass.getString('hello'),
'field2': DummyClass.getString('good'),
'field3': DummyClass.getOptionalString('goodbye'),
'field4': DummyClass.getOptionalString('go'),
}..removeWhere((k, v) => v == null);
print(myMap);
} catch (e) {
print('error condition, $e');
}
}

flutter - Comparing two lists of objects isn't working

I've two lists of objects that i wanna compare, a and b:
final dia = DateTime(2017, 9, 7, 17, 30);
final ppp = Parcela("1", 225.5, dia, null, 1, false, false);
final ppp2 =Parcela("1", 225, dia.add(const Duration(days: 3)), null, 1, false, false);
final ppp3 =Parcela("1", 225, dia.add(const Duration(days: 3)), null, 1, false, false);
List<Parcela> a = [ppp,ppp2,];
List<Parcela> b = [ppp, ppp3];
Both of them are equal, but when i try to check it with the functions bellow i get false on response:
print(a.every(b.toSet().contains));
print(listEquals(a, b));
I tried also "quiver" and "collection" libraries from pub dev but the result is the same
The Parcela model:
class Parcela {
String id;
double valor;
DateTime dataPagamento;
DateTime dataPago;
int status;
int ref;
Parcela(String id, double valor, DateTime dataPagamento, DateTime dataPago,
int ref, bool pago, bool atraso) {
this.id = id;
this.valor = valor;
this.dataPagamento = dataPagamento;
this.dataPago = this.dataPago;
this.status = _getStatus(pago, atraso);
this.ref = this.ref;
}
int _getStatus(bool pago, bool atraso) {
if (pago) {
if (atraso) {
return 3;
} else {
return 1;
}
} else {
if (atraso) {
return 2;
} else {
return 0;
}
}
}
}
Edit1:
I've tried Dan James suggestion but my class isn't final as his, so i've removed "final" from name attribute:
class Person extends Equatable {
Person(this.name);
String name;
#override
List<Object> get props => [name];
}
the new test vars:
Person p = Person("name");
Person p2 = Person("name2");
Person p3 = Person("tobias");
List<Person> aa = [p, p2];
List<Person> bb = [p, p2..name = "teste"];
List<Person> cc = [p, p3];
but when i test the lists:
var filtered_lst =List.from(aa.where((value) => !bb.contains(value)));
print(filtered_lst);
print(listEquals(aa, bb));
print(listEquals(aa, cc));
the console returns this:
I/flutter (12746): []
I/flutter (12746): true
I/flutter (12746): false
ppp2 does not equal ppp3 because they are two different instances of a class. You could override the '==' operator to check if each field is the same. ie. ppp2.id == ppp3.id.
eg/ (taken from equatable docs but this is vanillar dart)
class Person {
const Person(this.name);
final String name;
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;
#override
int get hashCode => name.hashCode;
}
Or look into the equatable package which does this for you. https://pub.dev/packages/equatable
Straight from the equatable docs:
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
#override
List<Object> get props => [name];
}
Using the library equatable from Dan James answer:
import 'package:equatable/equatable.dart';
// ignore: must_be_immutable
class Parcela extends Equatable {
String id;
double valor;
DateTime dataPagamento;
DateTime dataPago;
int status;
int ref;
Parcela(String id, double valor, DateTime dataPagamento, DateTime dataPago,
int ref, bool pago, bool atraso) {
this.id = id;
this.valor = valor;
this.dataPagamento = dataPagamento;
this.dataPago = this.dataPago;
this.status = _getStatus(pago, atraso);
this.ref = this.ref;
}
int _getStatus(bool pago, bool atraso) {
if (pago) {
if (atraso) {
return 3;
} else {
return 2;
}
} else {
if (atraso) {
return 1;
} else {
return 0;
}
}
}
#override
List<Object> get props => [id,valor,dataPagamento,];
}
The only way of copying a list that doesn't point to the same memory address:
List<Parcela> copyParcelas(List<Parcela> list) {
List<Parcela> copyList = [];
for (var item in list) {
List<bool> _status = pagoAtraso(item.status);
Parcela betItems = Parcela(item.id, item.valor, item.dataPagamento,
item.dataPago, item.ref, _status[0], _status[1]);
copyList.add(betItems);
}
return copyList;
}
Then the check to return the list items that changed:
List<Parcela> editarParcelas(List<Parcela> parcelas, List<Parcela> original){
return filteredlst = parcelas.toSet().difference(original.toSet()).toList();
}

Dart : Convert From Iterable<ActivityModel> To ActivityModel

I have model Like This :
Model
class ActivityModel {
String idActivity;
String titleActivity;
String dateTimeActivity;
int isDoneActivity;
int codeIconActivity;
String informationActivity;
String createdDateActivity;
ActivityModel({
this.idActivity,
this.titleActivity,
this.dateTimeActivity,
this.isDoneActivity,
this.codeIconActivity,
this.informationActivity,
this.createdDateActivity,
});
ActivityModel.fromSqflite(Map<String, dynamic> map)
: idActivity = map['id_activity'],
titleActivity = map['title_activity'],
dateTimeActivity = map['datetime_activity'],
isDoneActivity = map['is_done_activity'],
codeIconActivity = map['code_icon_activity'],
informationActivity = map['information_activity'],
createdDateActivity = map['created_date'];
Map<String, dynamic> toMapForSqflite() {
return {
'id_activity': this.idActivity,
'title_activity': this.titleActivity,
'datetime_activity': this.dateTimeActivity,
'is_done_activity': this.isDoneActivity,
'code_icon_activity': this.codeIconActivity,
'information_activity': this.informationActivity,
'created_date': this.createdDateActivity,
};
}
I want get data where dateTimeActivity is before than date now, then i update isDoneActivity = 1 with this code :
Source code
final passedDateItem = _selectedActivityItem.where((element) {
DateTime convertStringToDateTime =
DateTime.parse(element.dateTimeActivity);
return convertStringToDateTime.isBefore(DateTime.now());
});
if (passedDateItem != null) {
print('Not Null');
} else {
print('Nulledd');
return null;
}
The problem is , passedDateItem return Iterable[ActivityModel] , it's possible to convert it to ActivityModel? So i can easly update like this ?
if (passedDateItem != null) {
passedDateItem.isDoneActivity = 1; <<<
// return passedDateItem.map((e) => e.isDoneActivity = 1);
// final testtt= passedDateItem.
print('Not Null');
} else {
print('Nulledd');
return null;
}
Iterate through passedDateItem
for (var activityModel in passedDateItem) {
//..conditions
activityModel.isDoneActivity = 1;
}
If you are only interested in the first/last element of passedDateItem
use
passedDateItem.first.isDoneActivity == 1
or
passedDateItem.last.isDoneActivity == 1
make sure passedDateItem is not empty in that case.

About mybatis3.3 databaseId,the code is useless

I study the mybatis3.3 sources code and now I have a question:in the XMLMapperBuilder.databaseIdMatchesCurrent()
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
if (requiredDatabaseId != null) {
if (!requiredDatabaseId.equals(databaseId)) {
return false;
}
} else {
if (databaseId != null) {
return false;
}
// skip this fragment if there is a previous one with a not null databaseId
if (this.sqlFragments.containsKey(id)) {
XNode context = this.sqlFragments.get(id);
if (context.getStringAttribute("databaseId") != null) {
return false;
}
}
}
return true;
}
if the (requiredDatabaseId == null && databaseId != null) ,then the function will return false.
So the code
if (this.sqlFragments.containsKey(id)) {
XNode context = this.sqlFragments.get(id);
if (context.getStringAttribute("databaseId") != null) {
return false;
}
}
certainly no way to return false and this code is useless.
I just want to questioning: I understand this, right?

How to store user sql where clause, and how to apply it on a select?

I am using JPA / Eclipselink / PostgreSQL within my application.
I have a model that list some data, and I would like to let the user of the application to create his own where clause parameters.
How can I store theses parameters ? as plain sql string ?
Then how can I apply the where clause ? as a simple string concatenation ? (I don't like this idea at all).
Bests regards.
Ok, so I solved my problem.
For information : I have created a recursive JSON representation of every where clause parameters possibility.
And I have created a query using criteria api by decoding the pojo structure from json.
The json class look like that :
public class JSonSearchCriteria
{
public static enum CriteriaType
{
asc,
desc,
count,
countDistinct,
and,
or,
not,
equal,
notEqual,
between,
gt,
ge,
lt,
le,
like,
notLike;
}
#Expose
public CriteriaType type;
#Expose
public List<JSonSearchCriteria> sub;
#Expose
public String what = null;
#Expose
public List<Integer> integerValue = null;
#Expose
public List<Long> longValue = null;
#Expose
public List<Boolean> booleanValue = null;
#Expose
public List<String> stringValue = null;
#Expose
public List<DateTime> datetimeValue = null;
public JSonSearchCriteria()
{
}
public JSonSearchCriteria(final CriteriaType type)
{
this.type = type;
}
public JSonSearchCriteria(final CriteriaType type, final String what)
{
this(type);
this.what = what;
}
public JSonSearchCriteria(final CriteriaType type, final String what, final String... values)
{
this(type, what);
for(final String value : values)
{
value(value);
}
}
public JSonSearchCriteria(final CriteriaType type, final String what, final Long... values)
{
this(type, what);
for(final Long value : values)
{
value(value);
}
}
public JSonSearchCriteria(final CriteriaType type, final String what, final Integer... values)
{
this(type, what);
for(final Integer value : values)
{
value(value);
}
}
public JSonSearchCriteria(final CriteriaType type, final String what, final DateTime... values)
{
this(type, what);
for(final DateTime value : values)
{
value(value);
}
}
public void add(final JSonSearchCriteria subCriteria)
{
if(sub == null)
{
sub = new ArrayList<>();
}
sub.add(subCriteria);
}
public void value(final String value)
{
if(stringValue == null)
{
stringValue = new ArrayList<>();
}
stringValue.add(value);
}
public void value(final Long value)
{
if(longValue == null)
{
longValue = new ArrayList<>();
}
longValue.add(value);
}
public void value(final Integer value)
{
if(integerValue == null)
{
integerValue = new ArrayList<>();
}
integerValue.add(value);
}
public void value(final DateTime value)
{
if(datetimeValue == null)
{
datetimeValue = new ArrayList<>();
}
datetimeValue.add(value);
}
#SuppressWarnings(
{
"unchecked", "rawtypes"
})
#Transient
public Predicate buildPredicate(final CriteriaBuilder builder, final Root<Record> root, Join<Record, RecordInfo> infos)
{
switch(type)
{
case and:
case or:
final Predicate[] preds = new Predicate[sub.size()];
int cpt = 0;
for(final JSonSearchCriteria s : sub)
{
preds[cpt] = s.buildPredicate(builder, root, infos);
cpt++;
}
if(type == CriteriaType.and)
{
return builder.and(preds);
}
else if(type == CriteriaType.or)
{
return builder.or(preds);
}
break;
case equal:
case lt:
case gt:
case between:
final Path p;
if(what.startsWith("infos."))
{
p = infos.get(what.substring(6));
}
else
{
p = root.get(what);
}
if(stringValue != null && !stringValue.isEmpty())
{
if(type == CriteriaType.equal)
{
return builder.equal(p, stringValue.get(0));
}
}
else if(longValue != null && !longValue.isEmpty())
{
if(type == CriteriaType.equal)
{
return builder.equal(p, longValue.get(0));
}
else if(type == CriteriaType.lt)
{
return builder.lt(p, longValue.get(0));
}
else if(type == CriteriaType.gt)
{
return builder.gt(p, longValue.get(0));
}
}
else if(integerValue != null && !integerValue.isEmpty())
{
if(type == CriteriaType.equal)
{
return builder.equal(p, integerValue.get(0));
}
else if(type == CriteriaType.lt)
{
return builder.lt(p, integerValue.get(0));
}
else if(type == CriteriaType.gt)
{
return builder.gt(p, integerValue.get(0));
}
}
else if(booleanValue != null && !booleanValue.isEmpty())
{
return builder.equal(p, booleanValue.get(0));
}
else if(datetimeValue != null && !datetimeValue.isEmpty())
{
if(type == CriteriaType.equal)
{
return builder.equal(p, datetimeValue.get(0));
}
else if(type == CriteriaType.between && datetimeValue.size() > 1)
{
return builder.between(p, datetimeValue.get(0), datetimeValue.get(1));
}
}
break;
}
System.err.println(type + " - not implemented");
return null;
}
}
And it is used like that :
final SearchTemplate templ = DBHelper.get(SearchTemplate.class, 100);
final Gson gson = new GsonBuilder().registerTypeAdapter(DateTime.class, new DateTimeJsonAdapter()).create();
final JSonSearchCriteria crits = gson.fromJson(templ.getTemplate(), JSonSearchCriteria.class);
final CriteriaBuilder critBuilder = DBHelper.getInstance().em().getCriteriaBuilder();
final CriteriaQuery<Record> critQuery = critBuilder.createQuery(Record.class);
final Root<Record> root = critQuery.from(Record.class);
final Join<Record, RecordInfo> infos = root.join("infos");
critQuery.where(crits.buildPredicate(critBuilder, root, infos));
final TypedQuery<Record> query = DBHelper.getInstance().em().createQuery(critQuery);
final List<Record> result = query.getResultList();
for(final Record rec : result)
{
System.err.println(rec.toString());
}