hash/== with equatable

This commit is contained in:
Thibault Deckers 2021-07-28 17:38:22 +09:00
parent d04adf52a2
commit 62c199c745
35 changed files with 224 additions and 480 deletions

View file

@ -55,6 +55,10 @@ android {
defaultConfig {
applicationId appId
// minSdkVersion constraints:
// - Flutter & other plugins: 16
// - google_maps_flutter v2.0.5: 20
// - Aves native: 19
minSdkVersion 20
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()

View file

@ -1,6 +1,7 @@
import 'dart:ui' as ui show Codec;
import 'package:aves/services/android_app_service.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@ -49,23 +50,18 @@ class AppIconImage extends ImageProvider<AppIconImageKey> {
}
}
class AppIconImageKey {
@immutable
class AppIconImageKey extends Equatable {
final String packageName;
final double size;
final double scale;
@override
List<Object?> get props => [packageName, size, scale];
const AppIconImageKey({
required this.packageName,
required this.size,
this.scale = 1.0,
});
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is AppIconImageKey && other.packageName == packageName && other.size == size && other.scale == scale;
}
@override
int get hashCode => hashValues(packageName, size, scale);
}

View file

@ -3,6 +3,7 @@ import 'dart:math';
import 'dart:ui' as ui show Codec;
import 'package:aves/services/services.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@ -62,7 +63,8 @@ class RegionProvider extends ImageProvider<RegionProviderKey> {
void pause() => imageFileService.cancelRegion(key);
}
class RegionProviderKey {
@immutable
class RegionProviderKey extends Equatable {
// do not store the entry as it is, because the key should be constant
// but the entry attributes may change over time
final String uri, mimeType;
@ -72,6 +74,9 @@ class RegionProviderKey {
final Rectangle<int> region;
final Size imageSize;
@override
List<Object?> get props => [uri, pageId, rotationDegrees, isFlipped, sampleSize, region, imageSize];
const RegionProviderKey({
required this.uri,
required this.mimeType,
@ -83,24 +88,6 @@ class RegionProviderKey {
required this.imageSize,
});
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is RegionProviderKey && other.uri == uri && other.mimeType == mimeType && other.pageId == pageId && other.rotationDegrees == rotationDegrees && other.isFlipped == isFlipped && other.sampleSize == sampleSize && other.region == region && other.imageSize == imageSize;
}
@override
int get hashCode => hashValues(
uri,
mimeType,
pageId,
rotationDegrees,
isFlipped,
sampleSize,
region,
imageSize,
);
@override
String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, pageId=$pageId, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, sampleSize=$sampleSize, region=$region, imageSize=$imageSize}';
}

View file

@ -1,6 +1,7 @@
import 'dart:ui' as ui show Codec;
import 'package:aves/services/services.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@ -63,7 +64,8 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
void pause() => imageFileService.cancelThumbnail(key);
}
class ThumbnailProviderKey {
@immutable
class ThumbnailProviderKey extends Equatable {
// do not store the entry as it is, because the key should be constant
// but the entry attributes may change over time
final String uri, mimeType;
@ -73,6 +75,9 @@ class ThumbnailProviderKey {
final int dateModifiedSecs;
final double extent;
@override
List<Object?> get props => [uri, pageId, dateModifiedSecs, extent];
const ThumbnailProviderKey({
required this.uri,
required this.mimeType,
@ -83,20 +88,6 @@ class ThumbnailProviderKey {
this.extent = 0,
});
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ThumbnailProviderKey && other.uri == uri && other.pageId == pageId && other.dateModifiedSecs == dateModifiedSecs && other.extent == extent;
}
@override
int get hashCode => hashValues(
uri,
pageId,
dateModifiedSecs,
extent,
);
@override
String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, pageId=$pageId, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, dateModifiedSecs=$dateModifiedSecs, extent=$extent}';
}

View file

@ -3,15 +3,20 @@ import 'dart:ui' as ui show Codec;
import 'package:aves/services/services.dart';
import 'package:aves/utils/pedantic.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class UriImage extends ImageProvider<UriImage> {
@immutable
class UriImage extends ImageProvider<UriImage> with EquatableMixin {
final String uri, mimeType;
final int? pageId, rotationDegrees, expectedContentLength;
final bool isFlipped;
final double scale;
@override
List<Object?> get props => [uri, pageId, rotationDegrees, isFlipped, scale];
const UriImage({
required this.uri,
required this.mimeType,
@ -71,22 +76,6 @@ class UriImage extends ImageProvider<UriImage> {
}
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is UriImage && other.uri == uri && other.mimeType == mimeType && other.rotationDegrees == rotationDegrees && other.isFlipped == isFlipped && other.pageId == pageId && other.scale == scale;
}
@override
int get hashCode => hashValues(
uri,
mimeType,
rotationDegrees,
isFlipped,
pageId,
scale,
);
@override
String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, pageId=$pageId, scale=$scale}';
}

View file

@ -3,6 +3,7 @@ import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/services/services.dart';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@ -77,10 +78,13 @@ class Covers with ChangeNotifier {
}
@immutable
class CoverRow {
class CoverRow extends Equatable {
final CollectionFilter filter;
final int contentId;
@override
List<Object?> get props => [filter, contentId];
const CoverRow({
required this.filter,
required this.contentId,
@ -99,16 +103,4 @@ class CoverRow {
'filter': filter.toJson(),
'contentId': contentId,
};
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is CoverRow && other.filter == filter && other.contentId == contentId;
}
@override
int get hashCode => hashValues(filter, contentId);
@override
String toString() => '$runtimeType#${shortHash(this)}{filter=$filter, contentId=$contentId}';
}

View file

@ -1,7 +1,7 @@
import 'package:aves/model/entry.dart';
import 'package:aves/services/services.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
final Favourites favourites = Favourites._private();
@ -62,10 +62,13 @@ class Favourites with ChangeNotifier {
}
@immutable
class FavouriteRow {
class FavouriteRow extends Equatable {
final int contentId;
final String path;
@override
List<Object?> get props => [contentId, path];
const FavouriteRow({
required this.contentId,
required this.path,
@ -82,16 +85,4 @@ class FavouriteRow {
'contentId': contentId,
'path': path,
};
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is FavouriteRow && other.contentId == contentId && other.path == path;
}
@override
int get hashCode => hashValues(contentId, path);
@override
String toString() => '$runtimeType#${shortHash(this)}{contentId=$contentId, path=$path}';
}

View file

@ -16,6 +16,9 @@ class AlbumFilter extends CollectionFilter {
final String album;
final String? displayName;
@override
List<Object?> get props => [album];
const AlbumFilter(this.album, this.displayName);
AlbumFilter.fromMap(Map<String, dynamic> json)
@ -78,16 +81,4 @@ class AlbumFilter extends CollectionFilter {
// key `album-{path}` is expected by test driver
@override
String get key => '$type-$album';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is AlbumFilter && other.album == album;
}
@override
int get hashCode => hashValues(type, album);
@override
String toString() => '$runtimeType#${shortHash(this)}{album=$album}';
}

View file

@ -10,6 +10,9 @@ class FavouriteFilter extends CollectionFilter {
static const instance = FavouriteFilter._private();
@override
List<Object?> get props => [];
const FavouriteFilter._private();
@override
@ -37,13 +40,4 @@ class FavouriteFilter extends CollectionFilter {
@override
String get key => type;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is FavouriteFilter;
}
@override
int get hashCode => type.hashCode;
}

View file

@ -11,10 +11,12 @@ import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/filters/type.dart';
import 'package:aves/utils/color_utils.dart';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
abstract class CollectionFilter implements Comparable<CollectionFilter> {
@immutable
abstract class CollectionFilter extends Equatable implements Comparable<CollectionFilter> {
static const List<String> categoryOrder = [
QueryFilter.type,
FavouriteFilter.type,
@ -88,20 +90,15 @@ abstract class CollectionFilter implements Comparable<CollectionFilter> {
}
}
class FilterGridItem<T extends CollectionFilter> {
@immutable
class FilterGridItem<T extends CollectionFilter> with EquatableMixin {
final T filter;
final AvesEntry? entry;
@override
List<Object?> get props => [filter, entry?.uri];
const FilterGridItem(this.filter, this.entry);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is FilterGridItem && other.filter == filter && other.entry == entry;
}
@override
int get hashCode => hashValues(filter, entry);
}
typedef EntryFilter = bool Function(AvesEntry);

View file

@ -2,7 +2,6 @@ import 'package:aves/model/filters/filters.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class LocationFilter extends CollectionFilter {
@ -10,14 +9,17 @@ class LocationFilter extends CollectionFilter {
static const locationSeparator = ';';
final LocationLevel level;
String _location;
String? _countryCode;
late EntryFilter _test;
late final String _location;
late final String? _countryCode;
late final EntryFilter _test;
LocationFilter(this.level, this._location) {
final split = _location.split(locationSeparator);
if (split.isNotEmpty) _location = split[0];
if (split.length > 1) _countryCode = split[1];
@override
List<Object?> get props => [level, _location, _countryCode];
LocationFilter(this.level, String location) {
final split = location.split(locationSeparator);
_location = split.isNotEmpty ? split[0] : location;
_countryCode = split.length > 1 ? split[1] : null;
if (_location.isEmpty) {
_test = (entry) => !entry.hasGps;
@ -75,18 +77,6 @@ class LocationFilter extends CollectionFilter {
@override
String get key => '$type-$level-$_location';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is LocationFilter && other.level == level && other._location == _location;
}
@override
int get hashCode => hashValues(type, level, _location);
@override
String toString() => '$runtimeType#${shortHash(this)}{level=$level, location=$_location}';
// U+0041 Latin Capital letter A
// U+1F1E6 🇦 REGIONAL INDICATOR SYMBOL LETTER A
static const _countryCodeToFlagDiff = 0x1F1E6 - 0x0041;

View file

@ -3,20 +3,22 @@ import 'package:aves/ref/mime_types.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/utils/mime_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class MimeFilter extends CollectionFilter {
static const type = 'mime';
final String mime;
late EntryFilter _test;
late String _label;
late IconData _icon;
late final EntryFilter _test;
late final String _label;
late final IconData _icon;
static final image = MimeFilter(MimeTypes.anyImage);
static final video = MimeFilter(MimeTypes.anyVideo);
@override
List<Object?> get props => [mime];
MimeFilter(this.mime) {
IconData? icon;
var lowMime = mime.toLowerCase();
@ -73,16 +75,4 @@ class MimeFilter extends CollectionFilter {
@override
String get key => '$type-$mime';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is MimeFilter && other.mime == mime;
}
@override
int get hashCode => hashValues(type, mime);
@override
String toString() => '$runtimeType#${shortHash(this)}{mime=$mime}';
}

View file

@ -1,12 +1,13 @@
import 'package:aves/model/filters/filters.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class PathFilter extends CollectionFilter {
static const type = 'path';
final String path;
@override
List<Object?> get props => [path];
const PathFilter(this.path);
PathFilter.fromMap(Map<String, dynamic> json)
@ -31,16 +32,4 @@ class PathFilter extends CollectionFilter {
@override
String get key => '$type-$path';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is PathFilter && other.path == path;
}
@override
int get hashCode => hashValues(type, path);
@override
String toString() => '$runtimeType#${shortHash(this)}{path=$path}';
}

View file

@ -12,7 +12,10 @@ class QueryFilter extends CollectionFilter {
final String query;
final bool colorful;
late EntryFilter _test;
late final EntryFilter _test;
@override
List<Object?> get props => [query];
QueryFilter(this.query, {this.colorful = true}) {
var upQuery = query.toUpperCase();
@ -63,16 +66,4 @@ class QueryFilter extends CollectionFilter {
@override
String get key => '$type-$query';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is QueryFilter && other.query == query;
}
@override
int get hashCode => hashValues(type, query);
@override
String toString() => '$runtimeType#${shortHash(this)}{query=$query}';
}

View file

@ -1,14 +1,16 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class TagFilter extends CollectionFilter {
static const type = 'tag';
final String tag;
late EntryFilter _test;
late final EntryFilter _test;
@override
List<Object?> get props => [tag];
TagFilter(this.tag) {
if (tag.isEmpty) {
@ -49,16 +51,4 @@ class TagFilter extends CollectionFilter {
@override
String get key => '$type-$tag';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is TagFilter && other.tag == tag;
}
@override
int get hashCode => hashValues(type, tag);
@override
String toString() => '$runtimeType#${shortHash(this)}{tag=$tag}';
}

View file

@ -1,7 +1,6 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class TypeFilter extends CollectionFilter {
@ -14,8 +13,8 @@ class TypeFilter extends CollectionFilter {
static const _sphericalVideo = 'spherical_video'; // subset of videos
final String itemType;
late EntryFilter _test;
late IconData _icon;
late final EntryFilter _test;
late final IconData _icon;
static final animated = TypeFilter._private(_animated);
static final geotiff = TypeFilter._private(_geotiff);
@ -23,12 +22,19 @@ class TypeFilter extends CollectionFilter {
static final panorama = TypeFilter._private(_panorama);
static final sphericalVideo = TypeFilter._private(_sphericalVideo);
@override
List<Object?> get props => [itemType];
TypeFilter._private(this.itemType) {
switch (itemType) {
case _animated:
_test = (entry) => entry.isAnimated;
_icon = AIcons.animated;
break;
case _geotiff:
_test = (entry) => entry.isGeotiff;
_icon = AIcons.geo;
break;
case _motionPhoto:
_test = (entry) => entry.isMotionPhoto;
_icon = AIcons.motionPhoto;
@ -41,10 +47,6 @@ class TypeFilter extends CollectionFilter {
_test = (entry) => entry.isVideo && entry.is360;
_icon = AIcons.threeSixty;
break;
case _geotiff:
_test = (entry) => entry.isGeotiff;
_icon = AIcons.geo;
break;
}
}
@ -91,16 +93,4 @@ class TypeFilter extends CollectionFilter {
@override
String get key => '$type-$itemType';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is TypeFilter && other.itemType == itemType;
}
@override
int get hashCode => hashValues(type, itemType);
@override
String toString() => '$runtimeType#${shortHash(this)}{itemType=$itemType}';
}

View file

@ -1,41 +1,25 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
@immutable
class SectionKey {
const SectionKey();
}
class EntryAlbumSectionKey extends SectionKey {
class EntryAlbumSectionKey extends SectionKey with EquatableMixin {
final String? directory;
@override
List<Object?> get props => [directory];
const EntryAlbumSectionKey(this.directory);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is EntryAlbumSectionKey && other.directory == directory;
}
@override
int get hashCode => directory.hashCode;
@override
String toString() => '$runtimeType#${shortHash(this)}{directory=$directory}';
}
class EntryDateSectionKey extends SectionKey {
class EntryDateSectionKey extends SectionKey with EquatableMixin {
final DateTime? date;
@override
List<Object?> get props => [date];
const EntryDateSectionKey(this.date);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is EntryDateSectionKey && other.date == date;
}
@override
int get hashCode => date.hashCode;
@override
String toString() => '$runtimeType#${shortHash(this)}{date=$date}';
}

View file

@ -1,11 +1,15 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable
class ImageOpEvent {
class ImageOpEvent extends Equatable {
final bool success;
final String uri;
@override
List<Object?> get props => [success, uri];
const ImageOpEvent({
required this.success,
required this.uri,
@ -17,18 +21,6 @@ class ImageOpEvent {
uri: map['uri'],
);
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ImageOpEvent && other.success == success && other.uri == uri;
}
@override
int get hashCode => hashValues(success, uri);
@override
String toString() => '$runtimeType#${shortHash(this)}{success=$success, uri=$uri}';
}
class MoveOpEvent extends ImageOpEvent {
@ -55,6 +47,9 @@ class MoveOpEvent extends ImageOpEvent {
class ExportOpEvent extends MoveOpEvent {
final int? pageId;
@override
List<Object?> get props => [success, uri, pageId];
const ExportOpEvent({required bool success, required String uri, this.pageId, required Map newFields})
: super(
success: success,
@ -70,16 +65,4 @@ class ExportOpEvent extends MoveOpEvent {
newFields: map['newFields'] ?? {},
);
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ExportOpEvent && other.success == success && other.uri == uri && other.pageId == pageId;
}
@override
int get hashCode => hashValues(success, uri, pageId);
@override
String toString() => '$runtimeType#${shortHash(this)}{success=$success, uri=$uri, pageId=$pageId, newFields=$newFields}';
}

View file

@ -3,6 +3,7 @@ import 'package:aves/services/services.dart';
import 'package:aves/utils/change_notifier.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@ -155,9 +156,12 @@ class StorageVolume {
}
@immutable
class VolumeRelativeDirectory {
class VolumeRelativeDirectory extends Equatable {
final String volumePath, relativeDir;
@override
List<Object?> get props => [volumePath, relativeDir];
const VolumeRelativeDirectory({
required this.volumePath,
required this.relativeDir,
@ -187,13 +191,4 @@ class VolumeRelativeDirectory {
final volume = androidFileUtils.storageVolumes.firstWhereOrNull((volume) => volume.path == volumePath);
return volume?.getDescription(context) ?? volumePath;
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is VolumeRelativeDirectory && other.volumePath == volumePath && other.relativeDir == relativeDir;
}
@override
int get hashCode => hashValues(volumePath, relativeDir);
}

View file

@ -44,7 +44,6 @@ class Constants {
Dependency(
name: 'AndroidSVG',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/BigBadaboom/androidsvg/blob/master/LICENSE',
sourceUrl: 'https://github.com/BigBadaboom/androidsvg',
),
Dependency(
@ -56,19 +55,16 @@ class Constants {
Dependency(
name: 'CWAC-Document',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/commonsguy/cwac-document/blob/master/LICENSE',
sourceUrl: 'https://github.com/commonsguy/cwac-document',
),
Dependency(
name: 'Glide',
license: 'Apache 2.0, BSD 2-Clause',
licenseUrl: 'https://github.com/bumptech/glide/blob/master/LICENSE',
sourceUrl: 'https://github.com/bumptech/glide',
),
Dependency(
name: 'Metadata Extractor',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/drewnoakes/metadata-extractor/blob/master/LICENSE',
sourceUrl: 'https://github.com/drewnoakes/metadata-extractor',
),
];
@ -77,49 +73,44 @@ class Constants {
Dependency(
name: 'Connectivity Plus',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/connectivity_plus/LICENSE',
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/connectivity_plus/connectivity_plus/LICENSE',
sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/connectivity_plus',
),
Dependency(
name: 'FlutterFire (Core, Crashlytics)',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/FirebaseExtended/flutterfire/blob/master/LICENSE',
sourceUrl: 'https://github.com/FirebaseExtended/flutterfire',
),
Dependency(
name: 'fijkplayer (Aves fork)',
license: 'MIT',
licenseUrl: 'https://github.com/deckerst/fijkplayer/blob/master/LICENSE',
sourceUrl: 'https://github.com/deckerst/fijkplayer',
),
Dependency(
name: 'Google API Availability',
license: 'MIT',
licenseUrl: 'https://github.com/Baseflow/flutter-google-api-availability/blob/master/LICENSE',
sourceUrl: 'https://github.com/Baseflow/flutter-google-api-availability',
),
Dependency(
name: 'Google Maps for Flutter',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter/LICENSE',
sourceUrl: 'https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter',
sourceUrl: 'https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter',
),
Dependency(
name: 'Package Info Plus',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/LICENSE',
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/package_info_plus/package_info_plus/LICENSE',
sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus',
),
Dependency(
name: 'Permission Handler',
license: 'MIT',
licenseUrl: 'https://github.com/Baseflow/flutter-permission-handler/blob/develop/permission_handler/LICENSE',
sourceUrl: 'https://github.com/Baseflow/flutter-permission-handler',
),
Dependency(
name: 'Printing',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/DavBfr/dart_pdf/blob/master/LICENSE',
sourceUrl: 'https://github.com/DavBfr/dart_pdf',
),
Dependency(
@ -130,21 +121,19 @@ class Constants {
),
Dependency(
name: 'sqflite',
license: 'MIT',
licenseUrl: 'https://github.com/tekartik/sqflite/blob/master/sqflite/LICENSE',
license: 'BSD 2-Clause',
sourceUrl: 'https://github.com/tekartik/sqflite',
),
Dependency(
name: 'Streams Channel (Aves fork)',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/deckerst/aves_streams_channel/blob/master/LICENSE',
sourceUrl: 'https://github.com/deckerst/aves_streams_channel',
),
Dependency(
name: 'URL Launcher',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher/LICENSE',
sourceUrl: 'https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher',
sourceUrl: 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher',
),
];
@ -152,37 +141,31 @@ class Constants {
Dependency(
name: 'Charts',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/google/charts/blob/master/LICENSE',
sourceUrl: 'https://github.com/google/charts',
),
Dependency(
name: 'Decorated Icon',
license: 'MIT',
licenseUrl: 'https://github.com/benPesso/flutter_decorated_icon/blob/master/LICENSE',
sourceUrl: 'https://github.com/benPesso/flutter_decorated_icon',
),
Dependency(
name: 'Expansion Tile Card (Aves fork)',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/deckerst/expansion_tile_card/blob/master/LICENSE',
sourceUrl: 'https://github.com/deckerst/expansion_tile_card',
),
Dependency(
name: 'FlexColorPicker',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/rydmike/flex_color_picker/blob/master/LICENSE',
sourceUrl: 'https://github.com/rydmike/flex_color_picker',
),
Dependency(
name: 'Flutter Highlight',
license: 'MIT',
licenseUrl: 'https://github.com/git-touch/highlight/blob/master/LICENSE',
sourceUrl: 'https://github.com/git-touch/highlight',
),
Dependency(
name: 'Flutter Map',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/fleaflet/flutter_map/blob/master/LICENSE',
sourceUrl: 'https://github.com/fleaflet/flutter_map',
),
Dependency(
@ -194,19 +177,16 @@ class Constants {
Dependency(
name: 'Flutter Staggered Animations',
license: 'MIT',
licenseUrl: 'https://github.com/mobiten/flutter_staggered_animations/blob/master/LICENSE',
sourceUrl: 'https://github.com/mobiten/flutter_staggered_animations',
),
Dependency(
name: 'Material Design Icons Flutter',
license: 'MIT',
licenseUrl: 'https://github.com/ziofat/material_design_icons_flutter/blob/master/LICENSE',
sourceUrl: 'https://github.com/ziofat/material_design_icons_flutter',
),
Dependency(
name: 'Overlay Support',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/boyan01/overlay_support/blob/master/LICENSE',
sourceUrl: 'https://github.com/boyan01/overlay_support',
),
Dependency(
@ -218,19 +198,16 @@ class Constants {
Dependency(
name: 'Panorama',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/zesage/panorama/blob/master/LICENSE',
sourceUrl: 'https://github.com/zesage/panorama',
),
Dependency(
name: 'Percent Indicator',
license: 'BSD 2-Clause',
licenseUrl: 'https://github.com/diegoveloper/flutter_percent_indicator/blob/master/LICENSE',
sourceUrl: 'https://github.com/diegoveloper/flutter_percent_indicator/',
sourceUrl: 'https://github.com/diegoveloper/flutter_percent_indicator',
),
Dependency(
name: 'Provider',
license: 'MIT',
licenseUrl: 'https://github.com/rrousselGit/provider/blob/master/LICENSE',
sourceUrl: 'https://github.com/rrousselGit/provider',
),
];
@ -239,19 +216,21 @@ class Constants {
Dependency(
name: 'Collection',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/dart-lang/collection/blob/master/LICENSE',
sourceUrl: 'https://github.com/dart-lang/collection',
),
Dependency(
name: 'Country Code',
license: 'MIT',
licenseUrl: 'https://github.com/denixport/dart.country/blob/master/LICENSE',
sourceUrl: 'https://github.com/denixport/dart.country',
),
Dependency(
name: 'Equatable',
license: 'MIT',
sourceUrl: 'https://github.com/felangel/equatable',
),
Dependency(
name: 'Event Bus',
license: 'MIT',
licenseUrl: 'https://github.com/marcojakob/dart-event-bus/blob/master/LICENSE',
sourceUrl: 'https://github.com/marcojakob/dart-event-bus',
),
Dependency(
@ -263,49 +242,41 @@ class Constants {
Dependency(
name: 'Get It',
license: 'MIT',
licenseUrl: 'https://github.com/fluttercommunity/get_it/blob/master/LICENSE',
sourceUrl: 'https://github.com/fluttercommunity/get_it',
),
Dependency(
name: 'Github',
license: 'MIT',
licenseUrl: 'https://github.com/SpinlockLabs/github.dart/blob/master/LICENSE',
sourceUrl: 'https://github.com/SpinlockLabs/github.dart',
),
Dependency(
name: 'Intl',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/dart-lang/intl/blob/master/LICENSE',
sourceUrl: 'https://github.com/dart-lang/intl',
),
Dependency(
name: 'LatLong2',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/jifalops/dart-latlong/blob/master/LICENSE',
sourceUrl: 'https://github.com/jifalops/dart-latlong',
),
Dependency(
name: 'PDF for Dart and Flutter',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/DavBfr/dart_pdf/blob/master/LICENSE',
sourceUrl: 'https://github.com/DavBfr/dart_pdf',
),
Dependency(
name: 'Tuple',
license: 'BSD 2-Clause',
licenseUrl: 'https://github.com/dart-lang/tuple/blob/master/LICENSE',
sourceUrl: 'https://github.com/dart-lang/tuple',
),
Dependency(
name: 'Version',
license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/dartninja/version/blob/master/LICENSE',
sourceUrl: 'https://github.com/dartninja/version',
),
Dependency(
name: 'XML',
license: 'MIT',
licenseUrl: 'https://github.com/renggli/dart-xml/blob/master/LICENSE',
sourceUrl: 'https://github.com/renggli/dart-xml',
),
];
@ -320,7 +291,7 @@ class Dependency {
const Dependency({
required this.name,
required this.license,
required this.licenseUrl,
String? licenseUrl,
required this.sourceUrl,
});
}) : licenseUrl = licenseUrl ?? '$sourceUrl/blob/master/LICENSE';
}

View file

@ -15,6 +15,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
import 'package:aves/widgets/home_page.dart';
import 'package:aves/widgets/welcome_page.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
@ -50,6 +51,7 @@ class _AvesAppState extends State<AvesApp> {
@override
void initState() {
super.initState();
EquatableConfig.stringify = true;
initPlatformServices();
_appSetup = _setup();
_mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChange(event as String?));

View file

@ -3,6 +3,7 @@ import 'dart:math';
import 'package:aves/model/source/section_keys.dart';
import 'package:aves/theme/durations.dart';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
@ -221,13 +222,17 @@ class SectionedListLayout<T> {
String toString() => '$runtimeType#${shortHash(this)}{sectionCount=${sections.length} columnCount=$columnCount, tileExtent=$tileExtent}';
}
class SectionLayout {
@immutable
class SectionLayout extends Equatable {
final SectionKey sectionKey;
final int firstIndex, lastIndex, bodyFirstIndex;
final double minOffset, maxOffset, bodyMinOffset;
final double headerExtent, tileExtent, spacing, mainAxisStride;
final IndexedWidgetBuilder builder;
@override
List<Object?> get props => [sectionKey, firstIndex, lastIndex, minOffset, maxOffset, headerExtent, tileExtent, spacing];
const SectionLayout({
required this.sectionKey,
required this.firstIndex,
@ -263,15 +268,6 @@ class SectionLayout {
if (scrollOffset < 0) return firstIndex;
return bodyFirstIndex + (scrollOffset / mainAxisStride).ceil() - 1;
}
@override
bool operator ==(Object other) => identical(this, other) || other is SectionLayout && runtimeType == other.runtimeType && sectionKey == other.sectionKey && firstIndex == other.firstIndex && lastIndex == other.lastIndex && minOffset == other.minOffset && maxOffset == other.maxOffset && headerExtent == other.headerExtent && tileExtent == other.tileExtent && spacing == other.spacing;
@override
int get hashCode => hashValues(sectionKey, firstIndex, lastIndex, minOffset, maxOffset, headerExtent, tileExtent, spacing);
@override
String toString() => '$runtimeType#${shortHash(this)}{sectionKey=$sectionKey, firstIndex=$firstIndex, lastIndex=$lastIndex, minOffset=$minOffset, maxOffset=$maxOffset, headerExtent=$headerExtent, tileExtent=$tileExtent, spacing=$spacing}';
}
class _GridRow extends MultiChildRenderObjectWidget {

View file

@ -1,28 +1,22 @@
import 'dart:ui';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable
class MagnifierState {
const MagnifierState({
required this.position,
required this.scale,
required this.source,
});
class MagnifierState extends Equatable {
final Offset position;
final double? scale;
final ChangeSource source;
@override
bool operator ==(Object other) => identical(this, other) || other is MagnifierState && runtimeType == other.runtimeType && position == other.position && scale == other.scale;
List<Object?> get props => [position, scale, source];
@override
int get hashCode => hashValues(position, scale, source);
@override
String toString() => '$runtimeType#${shortHash(this)}{position: $position, scale: $scale, source: $source}';
const MagnifierState({
required this.position,
required this.scale,
required this.source,
});
}
enum ChangeSource { internal, gesture, animation }

View file

@ -6,6 +6,7 @@ import 'package:aves/widgets/common/magnifier/magnifier.dart';
import 'package:aves/widgets/common/magnifier/pan/corner_hit_detector.dart';
import 'package:aves/widgets/common/magnifier/scale/scale_boundaries.dart';
import 'package:aves/widgets/common/magnifier/scale/state.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
/// Internal widget in which controls all animations lifecycle, core responses
@ -276,17 +277,21 @@ class _MagnifierCoreState extends State<MagnifierCore> with TickerProviderStateM
}
}
class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate {
@immutable
class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate with EquatableMixin {
final Size subjectSize;
final Alignment basePosition;
final bool applyScale;
@override
List<Object?> get props => [subjectSize, basePosition, applyScale];
const _CenterWithOriginalSizeDelegate(
this.subjectSize,
this.basePosition,
this.applyScale,
);
final Size subjectSize;
final Alignment basePosition;
final bool applyScale;
@override
Offset getPositionForChild(Size size, Size childSize) {
final childWidth = applyScale ? subjectSize.width : childSize.width;
@ -309,10 +314,4 @@ class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate {
bool shouldRelayout(_CenterWithOriginalSizeDelegate oldDelegate) {
return oldDelegate != this;
}
@override
bool operator ==(Object other) => identical(this, other) || other is _CenterWithOriginalSizeDelegate && runtimeType == other.runtimeType && subjectSize == other.subjectSize && basePosition == other.basePosition && applyScale == other.applyScale;
@override
int get hashCode => hashValues(subjectSize, basePosition, applyScale);
}

View file

@ -2,17 +2,22 @@ import 'dart:ui';
import 'package:aves/widgets/common/magnifier/controller/controller.dart';
import 'package:aves/widgets/common/magnifier/scale/scale_level.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
/// Internal class to wrap custom scale boundaries (min, max and initial)
/// Also, stores values regarding the two sizes: the container and the child.
class ScaleBoundaries {
@immutable
class ScaleBoundaries extends Equatable {
final ScaleLevel _minScale;
final ScaleLevel _maxScale;
final ScaleLevel _initialScale;
final Size viewportSize;
final Size childSize;
@override
List<Object?> get props => [_minScale, _maxScale, _initialScale, viewportSize, childSize];
const ScaleBoundaries({
required ScaleLevel minScale,
required ScaleLevel maxScale,
@ -57,13 +62,4 @@ class ScaleBoundaries {
Offset childToStatePosition(double scale, Offset childPosition) {
return (_childCenter - childPosition) * scale;
}
@override
bool operator ==(Object other) => identical(this, other) || other is ScaleBoundaries && runtimeType == other.runtimeType && _minScale == other._minScale && _maxScale == other._maxScale && _initialScale == other._initialScale && viewportSize == other.viewportSize && childSize == other.childSize;
@override
int get hashCode => hashValues(_minScale, _maxScale, _initialScale, viewportSize, childSize);
@override
String toString() => '$runtimeType#${shortHash(this)}{viewportSize=$viewportSize, childSize=$childSize, initialScale=$initialScale, minScale=$minScale, maxScale=$maxScale}';
}

View file

@ -1,12 +1,17 @@
import 'dart:math';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
class ScaleLevel {
@immutable
class ScaleLevel extends Equatable {
final ScaleReference ref;
final double factor;
@override
List<Object?> get props => [ref, factor];
const ScaleLevel({
this.ref = ScaleReference.absolute,
this.factor = 1.0,
@ -15,18 +20,6 @@ class ScaleLevel {
static double scaleForContained(Size containerSize, Size childSize) => min(containerSize.width / childSize.width, containerSize.height / childSize.height);
static double scaleForCovering(Size containerSize, Size childSize) => max(containerSize.width / childSize.width, containerSize.height / childSize.height);
@override
String toString() => '$runtimeType#${shortHash(this)}{ref=$ref, factor=$factor}';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ScaleLevel && other.ref == ref && other.factor == factor;
}
@override
int get hashCode => hashValues(ref, factor);
}
enum ScaleReference { absolute, contained, covered }

View file

@ -1,29 +1,24 @@
import 'dart:ui';
import 'package:aves/widgets/common/magnifier/controller/state.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable
class ScaleStateChange {
const ScaleStateChange({
required this.state,
required this.source,
this.childFocalPoint,
});
class ScaleStateChange extends Equatable {
final ScaleState state;
final ChangeSource source;
final Offset? childFocalPoint;
@override
bool operator ==(Object other) => identical(this, other) || other is ScaleStateChange && runtimeType == other.runtimeType && state == other.state && childFocalPoint == other.childFocalPoint;
List<Object?> get props => [state, source, childFocalPoint];
@override
int get hashCode => hashValues(state, source, childFocalPoint);
@override
String toString() => '$runtimeType#${shortHash(this)}{scaleState: $state, source: $source, childFocalPoint: $childFocalPoint}';
const ScaleStateChange({
required this.state,
required this.source,
this.childFocalPoint,
});
}
enum ScaleState {

View file

@ -2,29 +2,20 @@ import 'package:aves/model/source/section_keys.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/foundation.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
class ChipSectionKey extends SectionKey {
class ChipSectionKey extends SectionKey with EquatableMixin {
final String title;
@override
List<Object?> get props => [title];
const ChipSectionKey({
this.title = '',
});
Widget? get leading => null;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ChipSectionKey && other.title == title;
}
@override
int get hashCode => title.hashCode;
@override
String toString() => '$runtimeType#${shortHash(this)}{title=$title}';
}
class AlbumImportanceSectionKey extends ChipSectionKey {

View file

@ -1,21 +1,17 @@
import 'package:aves/model/entry.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
class HeroInfo {
@immutable
class HeroInfo extends Equatable {
// hero tag should include a collection identifier, so that it animates
// between different views of the entry in the same collection (e.g. thumbnails <-> viewer)
// but not between different collection instances, even with the same attributes (e.g. reloading collection page via drawer)
final int? collectionId;
final AvesEntry? entry;
@override
List<Object?> get props => [collectionId, entry?.uri];
const HeroInfo(this.collectionId, this.entry);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is HeroInfo && other.collectionId == collectionId && other.entry == entry;
}
@override
int get hashCode => hashValues(collectionId, entry);
}

View file

@ -12,13 +12,18 @@ import 'package:aves/widgets/viewer/info/metadata/xmp_ns/photoshop.dart';
import 'package:aves/widgets/viewer/info/metadata/xmp_ns/tiff.dart';
import 'package:aves/widgets/viewer/info/metadata/xmp_ns/xmp.dart';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class XmpNamespace {
@immutable
class XmpNamespace extends Equatable {
final String namespace;
final Map<String, String> rawProps;
@override
List<Object?> get props => [namespace];
const XmpNamespace(this.namespace, this.rawProps);
factory XmpNamespace.create(String namespace, Map<String, String> rawProps) {
@ -119,20 +124,6 @@ class XmpNamespace {
String formatValue(XmpProp prop) => prop.value;
Map<String, InfoLinkHandler> linkifyValues(List<XmpProp> props) => {};
// identity
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is XmpNamespace && other.namespace == namespace;
}
@override
int get hashCode => namespace.hashCode;
@override
String toString() => '$runtimeType#${shortHash(this)}{namespace=$namespace}';
}
class XmpProp {

View file

@ -1,13 +1,17 @@
import 'package:aves/widgets/viewer/visual/subtitle/span.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable
class StyledSubtitleLine with Diagnosticable {
class StyledSubtitleLine extends Equatable with Diagnosticable {
final List<StyledSubtitleSpan> spans;
final List<Path>? clip;
final Offset? position;
@override
List<Object?> get props => [spans, clip, position];
const StyledSubtitleLine({
required this.spans,
this.clip,
@ -33,17 +37,4 @@ class StyledSubtitleLine with Diagnosticable {
properties.add(DiagnosticsProperty<List<Path>>('clip', clip));
properties.add(DiagnosticsProperty<Offset>('position', position));
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is StyledSubtitleLine && other.spans == spans && other.clip == clip && other.position == position;
}
@override
int get hashCode => hashValues(
spans,
clip,
position,
);
}

View file

@ -1,12 +1,16 @@
import 'package:aves/widgets/viewer/visual/subtitle/style.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable
class StyledSubtitleSpan with Diagnosticable {
class StyledSubtitleSpan extends Equatable with Diagnosticable {
final TextSpan textSpan;
final SubtitleStyle extraStyle;
@override
List<Object?> get props => [textSpan, extraStyle];
const StyledSubtitleSpan({
required this.textSpan,
required this.extraStyle,
@ -28,16 +32,4 @@ class StyledSubtitleSpan with Diagnosticable {
properties.add(DiagnosticsProperty<TextSpan>('textSpan', textSpan));
properties.add(DiagnosticsProperty<SubtitleStyle>('extraStyle', extraStyle));
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is StyledSubtitleSpan && other.textSpan == textSpan && other.extraStyle == extraStyle;
}
@override
int get hashCode => hashValues(
textSpan,
extraStyle,
);
}

View file

@ -1,8 +1,9 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable
class SubtitleStyle with Diagnosticable {
class SubtitleStyle extends Equatable with Diagnosticable {
final TextAlign? hAlign;
final TextAlignVertical? vAlign;
final Color? borderColor;
@ -15,6 +16,23 @@ class SubtitleStyle with Diagnosticable {
bool get shearing => (shearX ?? 0) != 0 || (shearY ?? 0) != 0;
@override
List<Object?> get props => [
hAlign,
vAlign,
borderColor,
borderWidth,
edgeBlur,
rotationX,
rotationY,
rotationZ,
scaleX,
scaleY,
shearX,
shearY,
drawingPaths?.length,
];
const SubtitleStyle({
this.hAlign,
this.vAlign,
@ -80,27 +98,4 @@ class SubtitleStyle with Diagnosticable {
properties.add(DoubleProperty('shearY', shearY));
properties.add(DiagnosticsProperty<List<Path>>('drawingPaths', drawingPaths));
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is SubtitleStyle && other.hAlign == hAlign && other.vAlign == vAlign && other.borderColor == borderColor && other.borderWidth == borderWidth && other.edgeBlur == edgeBlur && other.rotationX == rotationX && other.rotationY == rotationY && other.rotationZ == rotationZ && other.scaleX == scaleX && other.scaleY == scaleY && other.shearX == shearX && other.shearY == shearY && other.drawingPaths == drawingPaths;
}
@override
int get hashCode => hashValues(
hAlign,
vAlign,
borderColor,
borderWidth,
edgeBlur,
rotationX,
rotationY,
rotationZ,
scaleX,
scaleY,
shearX,
shearY,
drawingPaths?.length,
);
}

View file

@ -183,6 +183,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
equatable:
dependency: "direct main"
description:
name: equatable
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
event_bus:
dependency: "direct main"
description:

View file

@ -17,6 +17,7 @@ dependencies:
connectivity_plus:
country_code:
decorated_icon:
equatable:
event_bus:
expansion_tile_card:
git: