1

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);
deczaloth
  • 7,094
  • 4
  • 24
  • 59
Rahul
  • 3,529
  • 1
  • 5
  • 19
  • Have you tried to add a const private constructor to your ```AppLanguage```. In order to add custom methods/getters to a freezed class you need a const empty constructor. Check this: https://pub.dev/packages/freezed#adding-getters-and-methods-to-our-models – danypata Feb 12 '23 at 19:23
  • @danypata it worked. Can you post it as answer so that I could accept it? – Rahul Feb 13 '23 at 08:12
  • posted as an answer, feel free to accept it. – danypata Feb 13 '23 at 16:35

2 Answers2

1

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.

danypata
  • 9,895
  • 1
  • 31
  • 44
1

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;
   }
 }
}
Alex Sunder Singh
  • 2,378
  • 1
  • 7
  • 17