Skip to content

Instantly share code, notes, and snippets.

@ehbc221
Last active June 8, 2023 16:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ehbc221/916b4dff95d71c758cd7bc481b7b1be8 to your computer and use it in GitHub Desktop.
Save ehbc221/916b4dff95d71c758cd7bc481b7b1be8 to your computer and use it in GitHub Desktop.
A series of useful filters to handle searching via APIs in Flutter. Inspired from Spring boot backend ones.
import 'package:equatable/equatable.dart';
import 'package:expenza/domain/models/type/json_map.type.dart';
import 'package:expenza/mixins/to_json_field.mixin.dart';
import 'package:expenza/network/service/filter/filter.dart';
/// Filter a [bool].
///
/// - author - @ehbc221
/// - version - 1.0.0
/// - last updated - June 8th, 2023
class BoolFilter extends Filter<bool>
with EquatableMixin, ToJsonFieldMixin<Filter<bool>> {
const BoolFilter({
bool? equals,
bool? notEquals,
bool? specified,
List<bool>? inn,
List<bool>? notIn,
}) : super(
equals: equals,
notEquals: notEquals,
specified: specified,
inn: inn,
notIn: notIn,
);
factory BoolFilter.fromFilter(BoolFilter filter) => BoolFilter(
equals: filter.equals,
notEquals: filter.notEquals,
specified: filter.specified,
inn: filter.inn,
notIn: filter.notIn,
);
factory BoolFilter.fromJson(JsonMap json) => BoolFilter(
equals: json['equals'] as bool?,
notEquals: json['notEquals'] as bool?,
specified: json['specified'] as bool?,
inn: (json['in'] as List<dynamic>?)
?.map((dynamic e) => e as bool)
.toList(),
notIn: (json['notIn'] as List<dynamic>?)
?.map((dynamic e) => e as bool)
.toList(),
);
@override
BoolFilter copy() => BoolFilter.fromFilter(this);
@override
BoolFilter copyWith({
bool? equals,
bool? notEquals,
bool? specified,
List<bool>? inn,
List<bool>? notIn,
}) =>
BoolFilter(
equals: equals ?? this.equals,
notEquals: notEquals ?? this.notEquals,
specified: specified ?? this.specified,
inn: inn ?? this.inn,
notIn: notIn ?? this.notIn,
);
}
import 'package:equatable/equatable.dart';
import 'package:expenza/domain/models/type/json_map.type.dart';
import 'package:expenza/mixins/to_json_field.mixin.dart';
/// Filter data.
///
/// - author - @ehbc221
/// - version - 1.0.0
/// - last updated - June 8th, 2023
class Filter<FIELD_TYPE>
with EquatableMixin, ToJsonFieldMixin<Filter<FIELD_TYPE>> {
const Filter({
this.equals,
this.notEquals,
this.specified,
this.inn,
this.notIn,
});
factory Filter.fromFilter(Filter<FIELD_TYPE> filter) => Filter<FIELD_TYPE>(
equals: filter.equals,
notEquals: filter.notEquals,
specified: filter.specified,
inn: filter.inn,
notIn: filter.notIn,
);
final FIELD_TYPE? equals;
final FIELD_TYPE? notEquals;
final bool? specified;
final List<FIELD_TYPE>? inn;
final List<FIELD_TYPE>? notIn;
@override
List<Object?> get props => <Object?>[
equals,
notEquals,
specified,
inn,
notIn,
];
Filter<FIELD_TYPE> copy() => Filter<FIELD_TYPE>.fromFilter(this);
Filter<FIELD_TYPE> copyWith({
FIELD_TYPE? equals,
FIELD_TYPE? notEquals,
bool? specified,
List<FIELD_TYPE>? inn,
List<FIELD_TYPE>? notIn,
}) =>
Filter<FIELD_TYPE>(
equals: equals ?? this.equals,
notEquals: notEquals ?? this.notEquals,
specified: specified ?? this.specified,
inn: inn ?? this.inn,
notIn: notIn ?? this.notIn,
);
/// Convert this to a json field.
@override
JsonMap toJsonField(String fieldName) {
JsonMap json = <String, dynamic>{};
if (equals != null) {
json.addAll(<String, dynamic>{'$fieldName.equals': equals!});
}
if (notEquals != null) {
json.addAll(<String, dynamic>{'$fieldName.notEquals': notEquals!});
}
if (specified != null) {
json.addAll(<String, dynamic>{'$fieldName.specified': specified!});
}
if (inn != null) {
json.addAll(<String, dynamic>{'$fieldName.in': inn!});
}
if (notIn != null) {
json.addAll(<String, dynamic>{'$fieldName.notIn': notIn!});
}
return json;
}
}
import 'package:equatable/equatable.dart';
import 'package:expenza/domain/models/type/json_map.type.dart';
import 'package:expenza/mixins/to_json_field.mixin.dart';
import 'package:expenza/network/service/filter/filter.dart';
import 'package:expenza/network/service/filter/range.filter.dart';
/// Filter a [int].
///
/// - author - @ehbc221
/// - version - 1.0.0
/// - last updated - June 8th, 2023
class IntFilter extends RangeFilter<num>
with EquatableMixin, ToJsonFieldMixin<Filter<num>> {
const IntFilter({
num? equals,
num? notEquals,
bool? specified,
List<num>? inn,
List<num>? notIn,
num? greaterThan,
num? lessThan,
num? greaterThanOrEqual,
num? lessThanOrEqual,
}) : super(
equals: equals,
notEquals: notEquals,
specified: specified,
inn: inn,
notIn: notIn,
greaterThan: greaterThan,
lessThan: lessThan,
greaterThanOrEqual: greaterThanOrEqual,
lessThanOrEqual: lessThanOrEqual,
);
factory IntFilter.fromFilter(IntFilter filter) => IntFilter(
equals: filter.equals,
notEquals: filter.notEquals,
specified: filter.specified,
inn: filter.inn,
notIn: filter.notIn,
greaterThan: filter.greaterThan,
lessThan: filter.lessThan,
greaterThanOrEqual: filter.greaterThanOrEqual,
lessThanOrEqual: filter.lessThanOrEqual,
);
factory IntFilter.fromJson(JsonMap json) => IntFilter(
equals: json['equals'] as num?,
notEquals: json['notEquals'] as num?,
specified: json['specified'] as bool?,
inn: (json['in'] as List<dynamic>?)
?.map((dynamic e) => e as num)
.toList(),
notIn: (json['notIn'] as List<dynamic>?)
?.map((dynamic e) => e as num)
.toList(),
greaterThan: json['greaterThan'] as num?,
lessThan: json['lessThan'] as num?,
greaterThanOrEqual: json['greaterThanOrEqual'] as num?,
lessThanOrEqual: json['lessThanOrEqual'] as num?,
);
@override
IntFilter copy() => IntFilter.fromFilter(this);
@override
IntFilter copyWith({
num? equals,
num? notEquals,
bool? specified,
List<num>? inn,
List<num>? notIn,
num? greaterThan,
num? lessThan,
num? greaterThanOrEqual,
num? lessThanOrEqual,
}) =>
IntFilter(
equals: equals ?? this.equals,
notEquals: notEquals ?? this.notEquals,
specified: specified ?? this.specified,
inn: inn ?? this.inn,
notIn: notIn ?? this.notIn,
greaterThan: greaterThan ?? this.greaterThan,
lessThan: lessThan ?? this.lessThan,
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
lessThanOrEqual: lessThanOrEqual ?? this.lessThanOrEqual,
);
}
import 'package:equatable/equatable.dart';
import 'package:expenza/domain/models/enum/period.enum.dart';
import 'package:expenza/domain/models/type/json_map.type.dart';
import 'package:expenza/mixins/to_json_field.mixin.dart';
import 'package:expenza/network/service/filter/index.dart';
import 'package:json_annotation/json_annotation.dart';
/// Filter for [Period].
///
/// - author - @ehbc221
/// - version - 1.0.0
/// - last updated - June 8th, 2023
class PeriodFilter extends Filter<Period>
with EquatableMixin, ToJsonFieldMixin<Filter<Period>> {
const PeriodFilter({
Period? equals,
Period? notEquals,
bool? specified,
List<Period>? inn,
List<Period>? notIn,
}) : super(
equals: equals,
notEquals: notEquals,
specified: specified,
inn: inn,
notIn: notIn,
);
factory PeriodFilter.fromFilter(PeriodFilter filter) =>
Filter<Period>.fromFilter(filter) as PeriodFilter;
factory PeriodFilter.fromJson(JsonMap json) => PeriodFilter(
equals: $enumDecodeNullable($PeriodEnumMap, json['equals']),
notEquals: $enumDecodeNullable($PeriodEnumMap, json['notEquals']),
specified: json['specified'] as bool?,
inn: (json['in'] as List<dynamic>?)
?.map(
(dynamic e) => $enumDecodeNullable($PeriodEnumMap, json['in'])!,
)
.toList(),
notIn: (json['notIn'] as List<dynamic>?)
?.map(
(dynamic e) =>
$enumDecodeNullable($PeriodEnumMap, json['notIn'])!,
)
.toList(),
);
@override
List<Object?> get props => <Object?>[
equals,
notEquals,
specified,
inn,
notIn,
];
@override
PeriodFilter copy() => PeriodFilter.fromFilter(this);
@override
JsonMap toJsonField(String fieldName) {
JsonMap json = super.toJsonField(fieldName);
if (equals != null) {
json.addAll(
<String, dynamic>{'$fieldName.equals': equals!.name.toUpperCase()},
);
}
if (notEquals != null) {
json.addAll(<String, dynamic>{
'$fieldName.notEquals': notEquals!.name.toUpperCase()
});
}
if (specified != null) {
json.addAll(<String, dynamic>{'$fieldName.specified': specified!});
}
if (inn != null) {
json.addAll(<String, dynamic>{
'$fieldName.in': inn!
.map(
(Period period) => period.name.toUpperCase(),
)
.toList()
});
}
if (notIn != null) {
json.addAll(<String, dynamic>{
'$fieldName.notIn': notIn!
.map(
(Period period) => period.name.toUpperCase(),
)
.toList()
});
}
return json;
}
}
import 'package:equatable/equatable.dart';
import 'package:expenza/domain/models/type/json_map.type.dart';
import 'package:expenza/mixins/to_json_field.mixin.dart';
import 'package:expenza/network/service/filter/filter.dart';
/// Filter data in range.
///
/// - author - @ehbc221
/// - version - 1.0.0
/// - last updated - June 8th, 2023
class RangeFilter<FIELD_TYPE extends Comparable<FIELD_TYPE>>
extends Filter<FIELD_TYPE>
with EquatableMixin, ToJsonFieldMixin<Filter<FIELD_TYPE>> {
const RangeFilter({
FIELD_TYPE? equals,
FIELD_TYPE? notEquals,
bool? specified,
List<FIELD_TYPE>? inn,
List<FIELD_TYPE>? notIn,
this.greaterThan,
this.lessThan,
this.greaterThanOrEqual,
this.lessThanOrEqual,
}) : super(
equals: equals,
notEquals: notEquals,
specified: specified,
inn: inn,
notIn: notIn,
);
factory RangeFilter.fromFilter(RangeFilter<FIELD_TYPE> filter) =>
RangeFilter<FIELD_TYPE>(
equals: filter.equals,
notEquals: filter.notEquals,
specified: filter.specified,
inn: filter.inn,
notIn: filter.notIn,
greaterThan: filter.greaterThan,
lessThan: filter.lessThan,
greaterThanOrEqual: filter.greaterThanOrEqual,
lessThanOrEqual: filter.lessThanOrEqual,
);
final FIELD_TYPE? greaterThan;
final FIELD_TYPE? lessThan;
final FIELD_TYPE? greaterThanOrEqual;
final FIELD_TYPE? lessThanOrEqual;
@override
List<Object?> get props => <Object?>[
greaterThan,
lessThan,
greaterThanOrEqual,
lessThanOrEqual,
];
@override
RangeFilter<FIELD_TYPE> copy() => RangeFilter<FIELD_TYPE>.fromFilter(this);
RangeFilter<FIELD_TYPE> copyWithRangeFilter({
FIELD_TYPE? equals,
FIELD_TYPE? notEquals,
bool? specified,
List<FIELD_TYPE>? inn,
List<FIELD_TYPE>? notIn,
FIELD_TYPE? greaterThan,
FIELD_TYPE? lessThan,
FIELD_TYPE? greaterThanOrEqual,
FIELD_TYPE? lessThanOrEqual,
}) =>
RangeFilter<FIELD_TYPE>(
equals: equals ?? this.equals,
notEquals: notEquals ?? this.notEquals,
specified: specified ?? this.specified,
inn: inn ?? this.inn,
notIn: notIn ?? this.notIn,
greaterThan: greaterThan ?? this.greaterThan,
lessThan: lessThan ?? this.lessThan,
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
lessThanOrEqual: lessThanOrEqual ?? this.lessThanOrEqual,
);
@override
JsonMap toJsonField(String fieldName) {
JsonMap json = super.toJsonField(fieldName);
if (greaterThan != null) {
json.addAll(<String, dynamic>{'$fieldName.greaterThan': greaterThan!});
}
if (lessThan != null) {
json.addAll(
<String, dynamic>{'$fieldName.lessThan': lessThan!},
);
}
if (greaterThanOrEqual != null) {
json.addAll(<String, dynamic>{
'$fieldName.greaterThanOrEqual': greaterThanOrEqual!
});
}
if (lessThanOrEqual != null) {
json.addAll(
<String, dynamic>{'$fieldName.lessThanOrEqual': lessThanOrEqual!},
);
}
return json;
}
}
import 'package:equatable/equatable.dart';
import 'package:expenza/domain/models/type/json_map.type.dart';
import 'package:expenza/mixins/to_json_field.mixin.dart';
import 'package:expenza/network/service/filter/filter.dart';
/// Filter a [String].
///
/// - author - @ehbc221
/// - version - 1.0.0
/// - last updated - June 8th, 2023
class StringFilter extends Filter<String>
with EquatableMixin, ToJsonFieldMixin<Filter<String>> {
const StringFilter({
String? equals,
String? notEquals,
bool? specified,
List<String>? inn,
List<String>? notIn,
this.contains,
this.doesNotContain,
}) : super(
equals: equals,
notEquals: notEquals,
specified: specified,
inn: inn,
notIn: notIn,
);
factory StringFilter.fromFilter(StringFilter filter) => StringFilter(
equals: filter.equals,
notEquals: filter.notEquals,
specified: filter.specified,
inn: filter.inn,
notIn: filter.notIn,
contains: filter.contains,
doesNotContain: filter.doesNotContain,
);
factory StringFilter.fromJson(JsonMap json) => StringFilter(
equals: json['equals'] as String?,
notEquals: json['notEquals'] as String?,
specified: json['specified'] as bool?,
inn: (json['in'] as List<dynamic>?)
?.map((dynamic e) => e as String)
.toList(),
notIn: (json['notIn'] as List<dynamic>?)
?.map((dynamic e) => e as String)
.toList(),
contains: json['contains'] as String?,
doesNotContain: json['doesNotContain'] as String?,
);
final String? contains;
final String? doesNotContain;
@override
List<Object?> get props => <Object?>[
equals,
notEquals,
specified,
inn,
notIn,
contains,
doesNotContain,
];
@override
StringFilter copy() => StringFilter.fromFilter(this);
@override
StringFilter copyWith({
String? equals,
String? notEquals,
bool? specified,
List<String>? inn,
List<String>? notIn,
}) =>
StringFilter(
equals: equals ?? this.equals,
notEquals: notEquals ?? this.notEquals,
specified: specified ?? this.specified,
inn: inn ?? this.inn,
notIn: notIn ?? this.notIn,
);
@override
JsonMap toJsonField(String fieldName) {
JsonMap json = super.toJsonField(fieldName);
if (contains != null) {
json.addAll(<String, dynamic>{'$fieldName.contains': contains!});
}
if (doesNotContain != null) {
json.addAll(
<String, dynamic>{'$fieldName.doesNotContain': doesNotContain!},
);
}
return json;
}
}
import 'package:equatable/equatable.dart';
import 'package:expenza/domain/models/type/json_map.type.dart';
import 'package:expenza/mixins/to_json_field.mixin.dart';
import 'package:expenza/network/service/filter/filter.dart';
import 'package:expenza/network/service/filter/range.filter.dart';
import 'package:expenza/utils/date.utils.dart';
/// Filter a [DateTime].
///
/// - author - @ehbc221
/// - version - 1.0.0
/// - last updated - June 8th, 2023
class ZonedDateTimeFilter extends RangeFilter<DateTime>
with EquatableMixin, ToJsonFieldMixin<Filter<DateTime>> {
const ZonedDateTimeFilter({
DateTime? equals,
DateTime? notEquals,
bool? specified,
List<DateTime>? inn,
List<DateTime>? notIn,
DateTime? greaterThan,
DateTime? lessThan,
DateTime? greaterThanOrEqual,
DateTime? lessThanOrEqual,
}) : super(
equals: equals,
notEquals: notEquals,
specified: specified,
inn: inn,
notIn: notIn,
greaterThan: greaterThan,
lessThan: lessThan,
greaterThanOrEqual: greaterThanOrEqual,
lessThanOrEqual: lessThanOrEqual,
);
factory ZonedDateTimeFilter.fromFilter(ZonedDateTimeFilter filter) =>
ZonedDateTimeFilter(
equals: filter.equals,
notEquals: filter.notEquals,
specified: filter.specified,
inn: filter.inn,
notIn: filter.notIn,
greaterThan: filter.greaterThan,
lessThan: filter.lessThan,
greaterThanOrEqual: filter.greaterThanOrEqual,
lessThanOrEqual: filter.lessThanOrEqual,
);
factory ZonedDateTimeFilter.fromJson(JsonMap json) => ZonedDateTimeFilter(
equals: json['equals'] as DateTime?,
notEquals: json['notEquals'] as DateTime?,
specified: json['specified'] as bool?,
inn: (json['inn'] as List<dynamic>?)
?.map((dynamic e) => e as DateTime)
.toList(),
notIn: (json['notIn'] as List<dynamic>?)
?.map((dynamic e) => e as DateTime)
.toList(),
greaterThan: json['greaterThan'] as DateTime?,
lessThan: json['lessThan'] as DateTime?,
greaterThanOrEqual: json['greaterThanOrEqual'] as DateTime?,
lessThanOrEqual: json['lessThanOrEqual'] as DateTime?,
);
@override
ZonedDateTimeFilter copy() => ZonedDateTimeFilter.fromFilter(this);
@override
ZonedDateTimeFilter copyWith({
DateTime? equals,
DateTime? notEquals,
bool? specified,
List<DateTime>? inn,
List<DateTime>? notIn,
DateTime? greaterThan,
DateTime? lessThan,
DateTime? greaterThanOrEqual,
DateTime? lessThanOrEqual,
}) =>
ZonedDateTimeFilter(
equals: equals ?? this.equals,
notEquals: notEquals ?? this.notEquals,
specified: specified ?? this.specified,
inn: inn ?? this.inn,
notIn: notIn ?? this.notIn,
greaterThan: greaterThan ?? this.greaterThan,
lessThan: lessThan ?? this.lessThan,
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
lessThanOrEqual: lessThanOrEqual ?? this.lessThanOrEqual,
);
@override
JsonMap toJsonField(String fieldName) {
JsonMap json = <String, dynamic>{};
if (equals != null) {
json.addAll(<String, dynamic>{
'$fieldName.equals': formatJsonDateTimeForJson(equals!)
});
}
if (notEquals != null) {
json.addAll(<String, dynamic>{
'$fieldName.notEquals': formatJsonDateTimeForJson(notEquals!)
});
}
if (specified != null) {
json.addAll(<String, dynamic>{'$fieldName.specified': specified!});
}
if (inn != null) {
json.addAll(<String, dynamic>{
'$fieldName.inn': List<String?>.generate(
inn!.length,
(int index) => formatJsonDateTimeForJson(inn![index]),
)
});
}
if (notIn != null) {
json.addAll(<String, dynamic>{
'$fieldName.notIn': List<String?>.generate(
notIn!.length,
(int index) => formatJsonDateTimeForJson(notIn![index]),
)
});
}
if (greaterThan != null) {
json.addAll(<String, dynamic>{
'$fieldName.greaterThan': formatJsonDateTimeForJson(greaterThan!)
});
}
if (lessThan != null) {
json.addAll(
<String, dynamic>{
'$fieldName.lessThan': formatJsonDateTimeForJson(lessThan!)
},
);
}
if (greaterThanOrEqual != null) {
json.addAll(<String, dynamic>{
'$fieldName.greaterThanOrEqual':
formatJsonDateTimeForJson(greaterThanOrEqual!)
});
}
if (lessThanOrEqual != null) {
json.addAll(
<String, dynamic>{
'$fieldName.lessThanOrEqual':
formatJsonDateTimeForJson(lessThanOrEqual!)
},
);
}
return json;
}
}
@ehbc221
Copy link
Author

ehbc221 commented Jun 8, 2023

Handling Enumerations

The PeriodFilter is an example for handling filters based on an enumeration, assuming that we have the following period.enum.dart:

import 'package:json_annotation/json_annotation.dart';

/// A Period.
@JsonEnum()
enum Period {
  @JsonValue('ONE_TIME')
  oneTime,
  @JsonValue('DAY')
  day,
  @JsonValue('WEEK')
  week,
  @JsonValue('MONTH')
  month,
  @JsonValue('TRIMESTER')
  trimester,
  @JsonValue('SEMESTER')
  semester,
  @JsonValue('YEAR')
  year,
}

const Map<Period, String> $PeriodEnumMap = <Period, String>{
  Period.oneTime: 'ONE_TIME',
  Period.day: 'DAY',
  Period.week: 'WEEK',
  Period.month: 'MONTH',
  Period.trimester: 'TRIMESTER',
  Period.semester: 'SEMESTER',
  Period.year: 'YEAR',
};

@ehbc221
Copy link
Author

ehbc221 commented Jun 8, 2023

Example

Assuming that you want to filter in a list of budgets, we'll take the name field as example:

final StringFilter? name;

Now, you can set a user searched String as name.contains = 'Test' and retrieve in your backend a name.contains key that holds Test as value

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment