diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index e77749ffd..5493fc284 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -17,6 +17,9 @@ PODS: - fluttertoast (0.0.2): - Flutter - Toast + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) - geolocator_apple (1.2.0): - Flutter - image_picker_ios (0.0.1): @@ -36,7 +39,7 @@ PODS: - FlutterMacOS - path_provider_ios (0.0.1): - Flutter - - permission_handler_apple (9.3.0): + - permission_handler_apple (9.1.1): - Flutter - photo_manager (2.0.0): - Flutter @@ -50,7 +53,7 @@ PODS: - FlutterMacOS - sqflite (0.0.3): - Flutter - - FlutterMacOS + - FMDB (>= 2.7.5) - Toast (4.0.0) - url_launcher_ios (0.0.1): - Flutter @@ -81,13 +84,14 @@ DEPENDENCIES: - photo_manager (from `.symlinks/plugins/photo_manager/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite (from `.symlinks/plugins/sqflite/darwin`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) SPEC REPOS: trunk: + - FMDB - MapLibre - ReachabilitySwift - SAMKeychain @@ -135,7 +139,7 @@ EXTERNAL SOURCES: shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: - :path: ".symlinks/plugins/sqflite/darwin" + :path: ".symlinks/plugins/sqflite/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" video_player_avfoundation: @@ -151,23 +155,24 @@ SPEC CHECKSUMS: flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef flutter_udid: a2482c67a61b9c806ef59dd82ed8d007f1b7ac04 flutter_web_auth: c25208760459cec375a3c39f6a8759165ca0fa4d - fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c + fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a geolocator_apple: 9157311f654584b9bb72686c55fc02a97b73f461 - image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425 + image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5 integration_test: 13825b8a9334a850581300559b8839134b124670 isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073 MapLibre: 620fc933c1d6029b33738c905c1490d024e5d4ef maplibre_gl: a2efec727dd340e4c65e26d2b03b584f14881fd9 package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 - permission_handler_apple: 036b856153a2b1f61f21030ff725f3e6fece2b78 + permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 - shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 - sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812 video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 @@ -175,4 +180,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart index 687e7aaac..f075280ae 100644 --- a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart @@ -42,7 +42,7 @@ class ImmichAssetGrid extends HookConsumerWidget { this.assetsPerRow, this.showStorageIndicator, this.listener, - this.margin = 5.0, + this.margin = 2.0, this.selectionActive = false, this.preselectedAssets, this.canDeselect = true, diff --git a/mobile/lib/modules/search/models/curated_content.dart b/mobile/lib/modules/search/models/curated_content.dart index df7cb032c..87e98bb75 100644 --- a/mobile/lib/modules/search/models/curated_content.dart +++ b/mobile/lib/modules/search/models/curated_content.dart @@ -1,15 +1,60 @@ -/// A wrapper for [CuratedLocationsResponseDto] objects -/// and [CuratedObjectsResponseDto] to be displayed in -/// a view -class CuratedContent { - /// The label to show associated with this curated object - final String label; - - /// The id to lookup the asset from the server - final String id; - - CuratedContent({ - required this.id, - required this.label, - }); -} +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +/// A wrapper for [CuratedLocationsResponseDto] objects +/// and [CuratedObjectsResponseDto] to be displayed in +/// a view +class CuratedContent { + /// The label to show associated with this curated object + final String label; + + /// The id to lookup the asset from the server + final String id; + + CuratedContent({ + required this.label, + required this.id, + }); + + CuratedContent copyWith({ + String? label, + String? id, + }) { + return CuratedContent( + label: label ?? this.label, + id: id ?? this.id, + ); + } + + Map toMap() { + return { + 'label': label, + 'id': id, + }; + } + + factory CuratedContent.fromMap(Map map) { + return CuratedContent( + label: map['label'] as String, + id: map['id'] as String, + ); + } + + String toJson() => json.encode(toMap()); + + factory CuratedContent.fromJson(String source) => + CuratedContent.fromMap(json.decode(source) as Map); + + @override + String toString() => 'CuratedContent(label: $label, id: $id)'; + + @override + bool operator ==(covariant CuratedContent other) { + if (identical(this, other)) return true; + + return other.label == label && other.id == id; + } + + @override + int get hashCode => label.hashCode ^ id.hashCode; +} diff --git a/mobile/lib/modules/search/models/search_filter.dart b/mobile/lib/modules/search/models/search_filter.dart new file mode 100644 index 000000000..337da9266 --- /dev/null +++ b/mobile/lib/modules/search/models/search_filter.dart @@ -0,0 +1,310 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:openapi/api.dart'; + +class SearchLocationFilter { + String? country; + String? state; + String? city; + SearchLocationFilter({ + this.country, + this.state, + this.city, + }); + + SearchLocationFilter copyWith({ + String? country, + String? state, + String? city, + }) { + return SearchLocationFilter( + country: country ?? this.country, + state: state ?? this.state, + city: city ?? this.city, + ); + } + + Map toMap() { + return { + 'country': country, + 'state': state, + 'city': city, + }; + } + + factory SearchLocationFilter.fromMap(Map map) { + return SearchLocationFilter( + country: map['country'] != null ? map['country'] as String : null, + state: map['state'] != null ? map['state'] as String : null, + city: map['city'] != null ? map['city'] as String : null, + ); + } + + String toJson() => json.encode(toMap()); + + factory SearchLocationFilter.fromJson(String source) => + SearchLocationFilter.fromMap(json.decode(source) as Map); + + @override + String toString() => + 'SearchLocationFilter(country: $country, state: $state, city: $city)'; + + @override + bool operator ==(covariant SearchLocationFilter other) { + if (identical(this, other)) return true; + + return other.country == country && + other.state == state && + other.city == city; + } + + @override + int get hashCode => country.hashCode ^ state.hashCode ^ city.hashCode; +} + +class SearchCameraFilter { + String? make; + String? model; + SearchCameraFilter({ + this.make, + this.model, + }); + + SearchCameraFilter copyWith({ + String? make, + String? model, + }) { + return SearchCameraFilter( + make: make ?? this.make, + model: model ?? this.model, + ); + } + + Map toMap() { + return { + 'make': make, + 'model': model, + }; + } + + factory SearchCameraFilter.fromMap(Map map) { + return SearchCameraFilter( + make: map['make'] != null ? map['make'] as String : null, + model: map['model'] != null ? map['model'] as String : null, + ); + } + + String toJson() => json.encode(toMap()); + + factory SearchCameraFilter.fromJson(String source) => + SearchCameraFilter.fromMap(json.decode(source) as Map); + + @override + String toString() => 'SearchCameraFilter(make: $make, model: $model)'; + + @override + bool operator ==(covariant SearchCameraFilter other) { + if (identical(this, other)) return true; + + return other.make == make && other.model == model; + } + + @override + int get hashCode => make.hashCode ^ model.hashCode; +} + +class SearchDateFilter { + DateTime? takenBefore; + DateTime? takenAfter; + SearchDateFilter({ + this.takenBefore, + this.takenAfter, + }); + + SearchDateFilter copyWith({ + DateTime? takenBefore, + DateTime? takenAfter, + }) { + return SearchDateFilter( + takenBefore: takenBefore ?? this.takenBefore, + takenAfter: takenAfter ?? this.takenAfter, + ); + } + + Map toMap() { + return { + 'takenBefore': takenBefore?.millisecondsSinceEpoch, + 'takenAfter': takenAfter?.millisecondsSinceEpoch, + }; + } + + factory SearchDateFilter.fromMap(Map map) { + return SearchDateFilter( + takenBefore: map['takenBefore'] != null + ? DateTime.fromMillisecondsSinceEpoch(map['takenBefore'] as int) + : null, + takenAfter: map['takenAfter'] != null + ? DateTime.fromMillisecondsSinceEpoch(map['takenAfter'] as int) + : null, + ); + } + + String toJson() => json.encode(toMap()); + + factory SearchDateFilter.fromJson(String source) => + SearchDateFilter.fromMap(json.decode(source) as Map); + + @override + String toString() => + 'SearchDateFilter(takenBefore: $takenBefore, takenAfter: $takenAfter)'; + + @override + bool operator ==(covariant SearchDateFilter other) { + if (identical(this, other)) return true; + + return other.takenBefore == takenBefore && other.takenAfter == takenAfter; + } + + @override + int get hashCode => takenBefore.hashCode ^ takenAfter.hashCode; +} + +class SearchDisplayFilters { + bool isNotInAlbum = false; + bool isArchive = false; + bool isFavorite = false; + SearchDisplayFilters({ + required this.isNotInAlbum, + required this.isArchive, + required this.isFavorite, + }); + + SearchDisplayFilters copyWith({ + bool? isNotInAlbum, + bool? isArchive, + bool? isFavorite, + }) { + return SearchDisplayFilters( + isNotInAlbum: isNotInAlbum ?? this.isNotInAlbum, + isArchive: isArchive ?? this.isArchive, + isFavorite: isFavorite ?? this.isFavorite, + ); + } + + Map toMap() { + return { + 'isNotInAlbum': isNotInAlbum, + 'isArchive': isArchive, + 'isFavorite': isFavorite, + }; + } + + factory SearchDisplayFilters.fromMap(Map map) { + return SearchDisplayFilters( + isNotInAlbum: map['isNotInAlbum'] as bool, + isArchive: map['isArchive'] as bool, + isFavorite: map['isFavorite'] as bool, + ); + } + + String toJson() => json.encode(toMap()); + + factory SearchDisplayFilters.fromJson(String source) => + SearchDisplayFilters.fromMap(json.decode(source) as Map); + + @override + String toString() => + 'SearchDisplayFilters(isNotInAlbum: $isNotInAlbum, isArchive: $isArchive, isFavorite: $isFavorite)'; + + @override + bool operator ==(covariant SearchDisplayFilters other) { + if (identical(this, other)) return true; + + return other.isNotInAlbum == isNotInAlbum && + other.isArchive == isArchive && + other.isFavorite == isFavorite; + } + + @override + int get hashCode => + isNotInAlbum.hashCode ^ isArchive.hashCode ^ isFavorite.hashCode; +} + +class SearchFilter { + String? context; + String? filename; + Set people; + SearchLocationFilter location; + SearchCameraFilter camera; + SearchDateFilter date; + SearchDisplayFilters display; + + // Enum + AssetType mediaType; + + SearchFilter({ + this.context, + this.filename, + required this.people, + required this.location, + required this.camera, + required this.date, + required this.display, + required this.mediaType, + }); + + SearchFilter copyWith({ + String? context, + String? filename, + Set? people, + SearchLocationFilter? location, + SearchCameraFilter? camera, + SearchDateFilter? date, + SearchDisplayFilters? display, + AssetType? mediaType, + }) { + return SearchFilter( + context: context ?? this.context, + filename: filename ?? this.filename, + people: people ?? this.people, + location: location ?? this.location, + camera: camera ?? this.camera, + date: date ?? this.date, + display: display ?? this.display, + mediaType: mediaType ?? this.mediaType, + ); + } + + @override + String toString() { + return 'SearchFilter(context: $context, filename: $filename, people: $people, location: $location, camera: $camera, date: $date, display: $display, mediaType: $mediaType)'; + } + + @override + bool operator ==(covariant SearchFilter other) { + if (identical(this, other)) return true; + + return other.context == context && + other.filename == filename && + other.people == people && + other.location == location && + other.camera == camera && + other.date == date && + other.display == display && + other.mediaType == mediaType; + } + + @override + int get hashCode { + return context.hashCode ^ + filename.hashCode ^ + people.hashCode ^ + location.hashCode ^ + camera.hashCode ^ + date.hashCode ^ + display.hashCode ^ + mediaType.hashCode; + } +} diff --git a/mobile/lib/modules/search/providers/paginated_search.provider.dart b/mobile/lib/modules/search/providers/paginated_search.provider.dart new file mode 100644 index 000000000..e20e37c52 --- /dev/null +++ b/mobile/lib/modules/search/providers/paginated_search.provider.dart @@ -0,0 +1,62 @@ +import 'package:immich_mobile/modules/asset_viewer/providers/render_list.provider.dart'; +import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; +import 'package:immich_mobile/modules/search/services/search.service.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'paginated_search.provider.g.dart'; + +@riverpod +class PaginatedSearch extends _$PaginatedSearch { + Future?> _search(SearchFilter filter, int page) async { + final service = ref.read(searchServiceProvider); + final result = await service.search(filter, page); + + return result; + } + + @override + Future> build() async { + return []; + } + + Future> getNextPage(SearchFilter filter, int nextPage) async { + state = const AsyncValue.loading(); + + final newState = await AsyncValue.guard(() async { + final assets = await _search(filter, nextPage); + + if (assets != null) { + return [...?state.value, ...assets]; + } + }); + + state = newState.valueOrNull == null + ? const AsyncValue.data([]) + : AsyncValue.data(newState.value!); + + return newState.valueOrNull ?? []; + } + + clear() { + state = const AsyncValue.data([]); + } +} + +@riverpod +AsyncValue paginatedSearchRenderList( + PaginatedSearchRenderListRef ref, +) { + final assets = ref.watch(paginatedSearchProvider).value; + + if (assets != null) { + return ref.watch( + renderListProviderWithGrouping( + (assets, GroupAssetsBy.none), + ), + ); + } else { + return const AsyncValue.loading(); + } +} diff --git a/mobile/lib/modules/search/providers/paginated_search.provider.g.dart b/mobile/lib/modules/search/providers/paginated_search.provider.g.dart new file mode 100644 index 000000000..3357be777 --- /dev/null +++ b/mobile/lib/modules/search/providers/paginated_search.provider.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'paginated_search.provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$paginatedSearchRenderListHash() => + r'c2cc2381ee6ea8f8e08d6d4c1289bbf0c6b9647e'; + +/// See also [paginatedSearchRenderList]. +@ProviderFor(paginatedSearchRenderList) +final paginatedSearchRenderListProvider = + AutoDisposeProvider>.internal( + paginatedSearchRenderList, + name: r'paginatedSearchRenderListProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$paginatedSearchRenderListHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef PaginatedSearchRenderListRef + = AutoDisposeProviderRef>; +String _$paginatedSearchHash() => r'8312f358261368cf2b5572b839fdd8f8fbe9a62e'; + +/// See also [PaginatedSearch]. +@ProviderFor(PaginatedSearch) +final paginatedSearchProvider = + AutoDisposeAsyncNotifierProvider>.internal( + PaginatedSearch.new, + name: r'paginatedSearchProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$paginatedSearchHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$PaginatedSearch = AutoDisposeAsyncNotifier>; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/mobile/lib/modules/search/providers/people.provider.dart b/mobile/lib/modules/search/providers/people.provider.dart index 6009ee53a..398d1122a 100644 --- a/mobile/lib/modules/search/providers/people.provider.dart +++ b/mobile/lib/modules/search/providers/people.provider.dart @@ -1,51 +1,49 @@ -import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; -import 'package:immich_mobile/modules/search/models/curated_content.dart'; -import 'package:immich_mobile/modules/search/services/person.service.dart'; -import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; -import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'people.provider.g.dart'; - -@riverpod -Future> getCuratedPeople( - GetCuratedPeopleRef ref, -) async { - final PersonService personService = ref.read(personServiceProvider); - - final curatedPeople = await personService.getCuratedPeople(); - - return curatedPeople - .map((p) => CuratedContent(id: p.id, label: p.name)) - .toList(); -} - -@riverpod -Future personAssets(PersonAssetsRef ref, String personId) async { - final PersonService personService = ref.read(personServiceProvider); - final assets = await personService.getPersonAssets(personId); - if (assets == null) { - return RenderList.empty(); - } - - final settings = ref.read(appSettingsServiceProvider); - final groupBy = - GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; - return await RenderList.fromAssets(assets, groupBy); -} - -@riverpod -Future updatePersonName( - UpdatePersonNameRef ref, - String personId, - String updatedName, -) async { - final PersonService personService = ref.read(personServiceProvider); - final person = await personService.updateName(personId, updatedName); - - if (person != null && person.name == updatedName) { - ref.invalidate(getCuratedPeopleProvider); - return true; - } - return false; -} +import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; +import 'package:immich_mobile/modules/search/services/person.service.dart'; +import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; +import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:openapi/api.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'people.provider.g.dart'; + +@riverpod +Future> getAllPeople( + GetAllPeopleRef ref, +) async { + final PersonService personService = ref.read(personServiceProvider); + + final people = await personService.getAllPeople(); + + return people; +} + +@riverpod +Future personAssets(PersonAssetsRef ref, String personId) async { + final PersonService personService = ref.read(personServiceProvider); + final assets = await personService.getPersonAssets(personId); + if (assets == null) { + return RenderList.empty(); + } + + final settings = ref.read(appSettingsServiceProvider); + final groupBy = + GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; + return await RenderList.fromAssets(assets, groupBy); +} + +@riverpod +Future updatePersonName( + UpdatePersonNameRef ref, + String personId, + String updatedName, +) async { + final PersonService personService = ref.read(personServiceProvider); + final person = await personService.updateName(personId, updatedName); + + if (person != null && person.name == updatedName) { + ref.invalidate(getAllPeopleProvider); + return true; + } + return false; +} diff --git a/mobile/lib/modules/search/providers/people.provider.g.dart b/mobile/lib/modules/search/providers/people.provider.g.dart index c13c2c160..c68f7a75f 100644 --- a/mobile/lib/modules/search/providers/people.provider.g.dart +++ b/mobile/lib/modules/search/providers/people.provider.g.dart @@ -6,23 +6,21 @@ part of 'people.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$getCuratedPeopleHash() => r'2a534553812abe69abce2c2e41aa62b8de16e9d0'; +String _$getAllPeopleHash() => r'4eff6666be5a74710d1e8587e01d8154310d85bd'; -/// See also [getCuratedPeople]. -@ProviderFor(getCuratedPeople) -final getCuratedPeopleProvider = - AutoDisposeFutureProvider>.internal( - getCuratedPeople, - name: r'getCuratedPeopleProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$getCuratedPeopleHash, +/// See also [getAllPeople]. +@ProviderFor(getAllPeople) +final getAllPeopleProvider = + AutoDisposeFutureProvider>.internal( + getAllPeople, + name: r'getAllPeopleProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$getAllPeopleHash, dependencies: null, allTransitiveDependencies: null, ); -typedef GetCuratedPeopleRef - = AutoDisposeFutureProviderRef>; +typedef GetAllPeopleRef = AutoDisposeFutureProviderRef>; String _$personAssetsHash() => r'1d6eff5ca3aa630b58c4dad9516193b21896984d'; /// Copied from Dart SDK @@ -172,7 +170,7 @@ class _PersonAssetsProviderElement String get personId => (origin as PersonAssetsProvider).personId; } -String _$updatePersonNameHash() => r'c7179a7cc558669c3b30b03fbca7782a42f2b6fd'; +String _$updatePersonNameHash() => r'7145aaaf6fc38fdafe3a283ebf3d3f4fd0774cd2'; /// See also [updatePersonName]. @ProviderFor(updatePersonName) diff --git a/mobile/lib/modules/search/providers/search_filter.provider.dart b/mobile/lib/modules/search/providers/search_filter.provider.dart new file mode 100644 index 000000000..1a4914b41 --- /dev/null +++ b/mobile/lib/modules/search/providers/search_filter.provider.dart @@ -0,0 +1,27 @@ +import 'package:immich_mobile/modules/search/services/search.service.dart'; +import 'package:openapi/api.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'search_filter.provider.g.dart'; + +@riverpod +Future> getSearchSuggestions( + GetSearchSuggestionsRef ref, + SearchSuggestionType type, { + String? locationCountry, + String? locationState, + String? make, + String? model, +}) async { + final SearchService service = ref.read(searchServiceProvider); + + final suggestions = await service.getSearchSuggestions( + type, + country: locationCountry, + state: locationState, + make: make, + model: model, + ); + + return suggestions ?? []; +} diff --git a/mobile/lib/modules/search/providers/search_filter.provider.g.dart b/mobile/lib/modules/search/providers/search_filter.provider.g.dart new file mode 100644 index 000000000..d5cdaa031 --- /dev/null +++ b/mobile/lib/modules/search/providers/search_filter.provider.g.dart @@ -0,0 +1,229 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'search_filter.provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$getSearchSuggestionsHash() => + r'bc1e9a1a060868f14e6eb970d2251dbfe39c6866'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// See also [getSearchSuggestions]. +@ProviderFor(getSearchSuggestions) +const getSearchSuggestionsProvider = GetSearchSuggestionsFamily(); + +/// See also [getSearchSuggestions]. +class GetSearchSuggestionsFamily extends Family>> { + /// See also [getSearchSuggestions]. + const GetSearchSuggestionsFamily(); + + /// See also [getSearchSuggestions]. + GetSearchSuggestionsProvider call( + SearchSuggestionType type, { + String? locationCountry, + String? locationState, + String? make, + String? model, + }) { + return GetSearchSuggestionsProvider( + type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ); + } + + @override + GetSearchSuggestionsProvider getProviderOverride( + covariant GetSearchSuggestionsProvider provider, + ) { + return call( + provider.type, + locationCountry: provider.locationCountry, + locationState: provider.locationState, + make: provider.make, + model: provider.model, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'getSearchSuggestionsProvider'; +} + +/// See also [getSearchSuggestions]. +class GetSearchSuggestionsProvider + extends AutoDisposeFutureProvider> { + /// See also [getSearchSuggestions]. + GetSearchSuggestionsProvider( + SearchSuggestionType type, { + String? locationCountry, + String? locationState, + String? make, + String? model, + }) : this._internal( + (ref) => getSearchSuggestions( + ref as GetSearchSuggestionsRef, + type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ), + from: getSearchSuggestionsProvider, + name: r'getSearchSuggestionsProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$getSearchSuggestionsHash, + dependencies: GetSearchSuggestionsFamily._dependencies, + allTransitiveDependencies: + GetSearchSuggestionsFamily._allTransitiveDependencies, + type: type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ); + + GetSearchSuggestionsProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.type, + required this.locationCountry, + required this.locationState, + required this.make, + required this.model, + }) : super.internal(); + + final SearchSuggestionType type; + final String? locationCountry; + final String? locationState; + final String? make; + final String? model; + + @override + Override overrideWith( + FutureOr> Function(GetSearchSuggestionsRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: GetSearchSuggestionsProvider._internal( + (ref) => create(ref as GetSearchSuggestionsRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + type: type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ), + ); + } + + @override + AutoDisposeFutureProviderElement> createElement() { + return _GetSearchSuggestionsProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is GetSearchSuggestionsProvider && + other.type == type && + other.locationCountry == locationCountry && + other.locationState == locationState && + other.make == make && + other.model == model; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, type.hashCode); + hash = _SystemHash.combine(hash, locationCountry.hashCode); + hash = _SystemHash.combine(hash, locationState.hashCode); + hash = _SystemHash.combine(hash, make.hashCode); + hash = _SystemHash.combine(hash, model.hashCode); + + return _SystemHash.finish(hash); + } +} + +mixin GetSearchSuggestionsRef on AutoDisposeFutureProviderRef> { + /// The parameter `type` of this provider. + SearchSuggestionType get type; + + /// The parameter `locationCountry` of this provider. + String? get locationCountry; + + /// The parameter `locationState` of this provider. + String? get locationState; + + /// The parameter `make` of this provider. + String? get make; + + /// The parameter `model` of this provider. + String? get model; +} + +class _GetSearchSuggestionsProviderElement + extends AutoDisposeFutureProviderElement> + with GetSearchSuggestionsRef { + _GetSearchSuggestionsProviderElement(super.provider); + + @override + SearchSuggestionType get type => + (origin as GetSearchSuggestionsProvider).type; + @override + String? get locationCountry => + (origin as GetSearchSuggestionsProvider).locationCountry; + @override + String? get locationState => + (origin as GetSearchSuggestionsProvider).locationState; + @override + String? get make => (origin as GetSearchSuggestionsProvider).make; + @override + String? get model => (origin as GetSearchSuggestionsProvider).model; +} +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/mobile/lib/modules/search/providers/search_result_page.provider.dart b/mobile/lib/modules/search/providers/search_result_page.provider.dart deleted file mode 100644 index e220cc69f..000000000 --- a/mobile/lib/modules/search/providers/search_result_page.provider.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/asset_viewer/providers/render_list.provider.dart'; -import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; -import 'package:immich_mobile/modules/search/models/search_result_page_state.model.dart'; - -import 'package:immich_mobile/modules/search/services/search.service.dart'; -import 'package:immich_mobile/shared/models/asset.dart'; - -class SearchResultPageNotifier extends StateNotifier { - SearchResultPageNotifier(this._searchService) - : super( - SearchResultPageState( - searchResult: [], - isError: false, - isLoading: true, - isSuccess: false, - isSmart: false, - ), - ); - - final SearchService _searchService; - - Future search(String searchTerm, {bool smartSearch = true}) async { - state = state.copyWith( - searchResult: [], - isError: false, - isLoading: true, - isSuccess: false, - ); - - List? assets = - await _searchService.searchAsset(searchTerm, smartSearch: smartSearch); - - if (assets != null) { - state = state.copyWith( - searchResult: assets, - isError: false, - isLoading: false, - isSuccess: true, - isSmart: smartSearch, - ); - } else { - state = state.copyWith( - searchResult: [], - isError: true, - isLoading: false, - isSuccess: false, - isSmart: smartSearch, - ); - } - } -} - -final searchResultPageProvider = - StateNotifierProvider( - (ref) { - return SearchResultPageNotifier(ref.watch(searchServiceProvider)); -}); - -final searchRenderListProvider = Provider((ref) { - final result = ref.watch(searchResultPageProvider); - return ref.watch( - renderListProviderWithGrouping( - (result.searchResult, result.isSmart ? GroupAssetsBy.none : null), - ), - ); -}); diff --git a/mobile/lib/modules/search/services/person.service.dart b/mobile/lib/modules/search/services/person.service.dart index 4f92e729f..884a01c9f 100644 --- a/mobile/lib/modules/search/services/person.service.dart +++ b/mobile/lib/modules/search/services/person.service.dart @@ -20,7 +20,7 @@ class PersonService { PersonService(this._apiService, this._db); - Future> getCuratedPeople() async { + Future> getAllPeople() async { try { final peopleResponseDto = await _apiService.personApi.getAllPeople(); return peopleResponseDto?.people ?? []; diff --git a/mobile/lib/modules/search/services/search.service.dart b/mobile/lib/modules/search/services/search.service.dart index 35249dec5..4d19657af 100644 --- a/mobile/lib/modules/search/services/search.service.dart +++ b/mobile/lib/modules/search/services/search.service.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:immich_mobile/shared/providers/db.provider.dart'; @@ -29,25 +30,92 @@ class SearchService { } } - Future?> searchAsset( - String searchTerm, { - bool smartSearch = true, + Future?> getSearchSuggestions( + SearchSuggestionType type, { + String? country, + String? state, + String? make, + String? model, }) async { - // TODO search in local DB: 1. when offline, 2. to find local assets try { - final SearchResponseDto? results = await _apiService.searchApi.search( - query: searchTerm, - smart: smartSearch, + return await _apiService.searchApi.getSearchSuggestions( + type, + country: country, + state: state, + make: make, + model: model, ); - if (results == null) { + } catch (e) { + debugPrint("[ERROR] [getSearchSuggestions] ${e.toString()}"); + return []; + } + } + + Future?> search(SearchFilter filter, int page) async { + try { + SearchResponseDto? response; + AssetTypeEnum? type; + if (filter.mediaType == AssetType.image) { + type = AssetTypeEnum.IMAGE; + } else if (filter.mediaType == AssetType.video) { + type = AssetTypeEnum.VIDEO; + } + + if (filter.context != null && filter.context!.isNotEmpty) { + response = await _apiService.searchApi.searchSmart( + SmartSearchDto( + query: filter.context!, + country: filter.location.country, + state: filter.location.state, + city: filter.location.city, + make: filter.camera.make, + model: filter.camera.model, + takenAfter: filter.date.takenAfter, + takenBefore: filter.date.takenBefore, + isArchived: filter.display.isArchive, + isFavorite: filter.display.isFavorite, + isNotInAlbum: filter.display.isNotInAlbum, + personIds: filter.people.map((e) => e.id).toList(), + type: type, + page: page, + size: 1000, + ), + ); + } else { + response = await _apiService.searchApi.searchMetadata( + MetadataSearchDto( + originalFileName: + filter.filename != null && filter.filename!.isNotEmpty + ? filter.filename + : null, + country: filter.location.country, + state: filter.location.state, + city: filter.location.city, + make: filter.camera.make, + model: filter.camera.model, + takenAfter: filter.date.takenAfter, + takenBefore: filter.date.takenBefore, + isArchived: filter.display.isArchive, + isFavorite: filter.display.isFavorite, + isNotInAlbum: filter.display.isNotInAlbum, + personIds: filter.people.map((e) => e.id).toList(), + type: type, + page: page, + size: 1000, + ), + ); + } + + if (response == null) { return null; } - // TODO local DB might be out of date; add assets not yet in DB? - return _db.assets.getAllByRemoteId(results.assets.items.map((e) => e.id)); - } catch (e) { - debugPrint("[ERROR] [searchAsset] ${e.toString()}"); - return null; + + return _db.assets + .getAllByRemoteId(response.assets.items.map((e) => e.id)); + } catch (error) { + debugPrint("Error [search] $error"); } + return null; } Future?> getCuratedLocation() async { diff --git a/mobile/lib/modules/search/ui/explore_grid.dart b/mobile/lib/modules/search/ui/explore_grid.dart index fd49fff7c..ba55b5581 100644 --- a/mobile/lib/modules/search/ui/explore_grid.dart +++ b/mobile/lib/modules/search/ui/explore_grid.dart @@ -1,8 +1,10 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/modules/search/models/curated_content.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; @@ -57,7 +59,22 @@ class ExploreGrid extends StatelessWidget { ), ) : context.pushRoute( - SearchResultRoute(searchTerm: 'm:${content.label}'), + SearchInputRoute( + prefilter: SearchFilter( + people: {}, + location: SearchLocationFilter( + city: content.label, + ), + camera: SearchCameraFilter(), + date: SearchDateFilter(), + display: SearchDisplayFilters( + isNotInAlbum: false, + isArchive: false, + isFavorite: false, + ), + mediaType: AssetType.other, + ), + ), ); }, ); diff --git a/mobile/lib/modules/search/ui/immich_search_bar.dart b/mobile/lib/modules/search/ui/immich_search_bar.dart deleted file mode 100644 index f4fa62d26..000000000 --- a/mobile/lib/modules/search/ui/immich_search_bar.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; - -class ImmichSearchBar extends HookConsumerWidget - implements PreferredSizeWidget { - const ImmichSearchBar({ - super.key, - required this.searchFocusNode, - required this.onSubmitted, - }); - - final FocusNode searchFocusNode; - final Function(String) onSubmitted; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final searchTermController = useTextEditingController(text: ""); - final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled; - - focusSearch() { - searchTermController.clear(); - ref.watch(searchPageStateProvider.notifier).getSuggestedSearchTerms(); - ref.watch(searchPageStateProvider.notifier).enableSearch(); - ref.watch(searchPageStateProvider.notifier).setSearchTerm(""); - - searchFocusNode.requestFocus(); - } - - useEffect( - () { - searchFocusNotifier.addListener(focusSearch); - return () { - searchFocusNotifier.removeListener(focusSearch); - }; - }, - [], - ); - - return AppBar( - automaticallyImplyLeading: false, - leading: isSearchEnabled - ? IconButton( - onPressed: () { - searchFocusNode.unfocus(); - ref.watch(searchPageStateProvider.notifier).disableSearch(); - searchTermController.clear(); - }, - icon: const Icon(Icons.arrow_back_ios_rounded), - ) - : const Icon( - Icons.search_rounded, - size: 20, - ), - title: TextField( - controller: searchTermController, - focusNode: searchFocusNode, - autofocus: false, - onTap: focusSearch, - onSubmitted: (searchTerm) { - onSubmitted(searchTerm); - searchTermController.clear(); - ref.watch(searchPageStateProvider.notifier).setSearchTerm(""); - }, - onChanged: (value) { - ref.watch(searchPageStateProvider.notifier).setSearchTerm(value); - }, - decoration: InputDecoration( - hintText: 'search_bar_hint'.tr(), - hintStyle: context.textTheme.bodyLarge?.copyWith( - color: context.themeData.colorScheme.onSurface.withOpacity(0.75), - ), - enabledBorder: const UnderlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const UnderlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - ), - ), - ); - } - - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); -} - -// Used to focus search from outside this widget. -// For example when double pressing the search nav icon. -final searchFocusNotifier = SearchFocusNotifier(); - -class SearchFocusNotifier with ChangeNotifier { - void requestFocus() { - notifyListeners(); - } -} diff --git a/mobile/lib/modules/search/ui/search_filter/camera_picker.dart b/mobile/lib/modules/search/ui/search_filter/camera_picker.dart new file mode 100644 index 000000000..fdfd398e6 --- /dev/null +++ b/mobile/lib/modules/search/ui/search_filter/camera_picker.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; +import 'package:immich_mobile/modules/search/providers/search_filter.provider.dart'; +import 'package:openapi/api.dart'; + +class CameraPicker extends HookConsumerWidget { + const CameraPicker({super.key, required this.onSelect, this.filter}); + + final Function(Map) onSelect; + final SearchCameraFilter? filter; + @override + Widget build(BuildContext context, WidgetRef ref) { + final makeTextController = useTextEditingController(text: filter?.make); + final modelTextController = useTextEditingController(text: filter?.model); + final selectedMake = useState(filter?.make); + final selectedModel = useState(filter?.model); + + final make = ref.watch( + getSearchSuggestionsProvider( + SearchSuggestionType.cameraMake, + ), + ); + + final models = ref.watch( + getSearchSuggestionsProvider( + SearchSuggestionType.cameraModel, + make: selectedMake.value, + ), + ); + + final inputDecorationTheme = InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + ), + contentPadding: const EdgeInsets.only(left: 16), + ); + + final menuStyle = MenuStyle( + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + ), + ); + + return Container( + padding: const EdgeInsets.only( + // bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DropdownMenu( + dropdownMenuEntries: switch (make) { + AsyncError() => [], + AsyncData(:final value) => value + .map( + (e) => DropdownMenuEntry( + value: e, + label: e, + ), + ) + .toList(), + _ => [], + }, + width: context.width * 0.45, + menuHeight: 400, + label: const Text('Make'), + inputDecorationTheme: inputDecorationTheme, + controller: makeTextController, + menuStyle: menuStyle, + leadingIcon: const Icon(Icons.photo_camera_rounded), + trailingIcon: const Icon(Icons.arrow_drop_down_rounded), + selectedTrailingIcon: const Icon(Icons.arrow_drop_up_rounded), + onSelected: (value) { + selectedMake.value = value.toString(); + onSelect({ + 'make': selectedMake.value, + 'model': selectedModel.value, + }); + }, + ), + DropdownMenu( + dropdownMenuEntries: switch (models) { + AsyncError() => [], + AsyncData(:final value) => value + .map( + (e) => DropdownMenuEntry( + value: e, + label: e, + ), + ) + .toList(), + _ => [], + }, + width: context.width * 0.45, + menuHeight: 400, + label: const Text('Model'), + inputDecorationTheme: inputDecorationTheme, + controller: modelTextController, + menuStyle: menuStyle, + leadingIcon: const Icon(Icons.camera), + trailingIcon: const Icon(Icons.arrow_drop_down_rounded), + selectedTrailingIcon: const Icon(Icons.arrow_drop_up_rounded), + onSelected: (value) { + selectedModel.value = value.toString(); + onSelect({ + 'make': selectedMake.value, + 'model': selectedModel.value, + }); + }, + ), + ], + ), + ); + } +} diff --git a/mobile/lib/modules/search/ui/search_filter/display_option_picker.dart b/mobile/lib/modules/search/ui/search_filter/display_option_picker.dart new file mode 100644 index 000000000..f6cd01cbb --- /dev/null +++ b/mobile/lib/modules/search/ui/search_filter/display_option_picker.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; + +enum DisplayOption { + notInAlbum, + favorite, + archive, +} + +class DisplayOptionPicker extends HookWidget { + const DisplayOptionPicker({ + super.key, + required this.onSelect, + this.filter, + }); + + final Function(Map) onSelect; + final SearchDisplayFilters? filter; + + @override + Widget build(BuildContext context) { + final options = useState>({ + DisplayOption.notInAlbum: filter?.isNotInAlbum ?? false, + DisplayOption.favorite: filter?.isFavorite ?? false, + DisplayOption.archive: filter?.isArchive ?? false, + }); + + return ListView( + shrinkWrap: true, + children: [ + CheckboxListTile( + title: const Text('Not in album'), + value: options.value[DisplayOption.notInAlbum], + onChanged: (bool? value) { + options.value = { + ...options.value, + DisplayOption.notInAlbum: value!, + }; + onSelect(options.value); + }, + ), + CheckboxListTile( + title: const Text('Favorite'), + value: options.value[DisplayOption.favorite], + onChanged: (value) { + options.value = { + ...options.value, + DisplayOption.favorite: value!, + }; + onSelect(options.value); + }, + ), + CheckboxListTile( + title: const Text('Archive'), + value: options.value[DisplayOption.archive], + onChanged: (value) { + options.value = { + ...options.value, + DisplayOption.archive: value!, + }; + onSelect(options.value); + }, + ), + ], + ); + } +} diff --git a/mobile/lib/modules/search/ui/search_filter/filter_bottom_sheet_scaffold.dart b/mobile/lib/modules/search/ui/search_filter/filter_bottom_sheet_scaffold.dart new file mode 100644 index 000000000..46bfe96bb --- /dev/null +++ b/mobile/lib/modules/search/ui/search_filter/filter_bottom_sheet_scaffold.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class FilterBottomSheetScaffold extends StatelessWidget { + const FilterBottomSheetScaffold({ + super.key, + required this.child, + required this.onSearch, + required this.onClear, + required this.title, + this.expanded, + }); + + final bool? expanded; + final String title; + final Widget child; + final Function() onSearch; + final Function() onClear; + + @override + Widget build(BuildContext context) { + buildChildWidget() { + if (expanded != null && expanded == true) { + return Expanded(child: child); + } + return child; + } + + return SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + title, + style: context.textTheme.headlineSmall, + ), + ), + buildChildWidget(), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + onPressed: () { + onClear(); + Navigator.of(context).pop(); + }, + child: const Text('Clear'), + ), + const SizedBox(width: 8), + ElevatedButton( + onPressed: () { + onSearch(); + Navigator.of(context).pop(); + }, + child: const Text('Apply filter'), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/modules/search/ui/search_filter/location_picker.dart b/mobile/lib/modules/search/ui/search_filter/location_picker.dart new file mode 100644 index 000000000..22568da47 --- /dev/null +++ b/mobile/lib/modules/search/ui/search_filter/location_picker.dart @@ -0,0 +1,166 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; +import 'package:immich_mobile/modules/search/providers/search_filter.provider.dart'; +import 'package:openapi/api.dart'; + +class LocationPicker extends HookConsumerWidget { + const LocationPicker({super.key, required this.onSelected, this.filter}); + + final Function(Map) onSelected; + final SearchLocationFilter? filter; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final countryTextController = + useTextEditingController(text: filter?.country); + final stateTextController = useTextEditingController(text: filter?.state); + final cityTextController = useTextEditingController(text: filter?.city); + + final selectedCountry = useState(filter?.country); + final selectedState = useState(filter?.state); + final selectedCity = useState(filter?.city); + + final countries = ref.watch( + getSearchSuggestionsProvider( + SearchSuggestionType.country, + locationCountry: selectedCountry.value, + locationState: selectedState.value, + ), + ); + + final states = ref.watch( + getSearchSuggestionsProvider( + SearchSuggestionType.state, + locationCountry: selectedCountry.value, + locationState: selectedState.value, + ), + ); + + final cities = ref.watch( + getSearchSuggestionsProvider( + SearchSuggestionType.city, + locationCountry: selectedCountry.value, + locationState: selectedState.value, + ), + ); + + final inputDecorationTheme = InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + ), + contentPadding: const EdgeInsets.only(left: 16), + ); + + final menuStyle = MenuStyle( + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + ), + ); + + return Column( + children: [ + DropdownMenu( + dropdownMenuEntries: switch (countries) { + AsyncError() => [], + AsyncData(:final value) => value + .map( + (e) => DropdownMenuEntry( + value: e, + label: e, + ), + ) + .toList(), + _ => [], + }, + menuHeight: 400, + width: context.width * 0.9, + label: const Text('Country'), + inputDecorationTheme: inputDecorationTheme, + menuStyle: menuStyle, + controller: countryTextController, + trailingIcon: const Icon(Icons.arrow_drop_down_rounded), + selectedTrailingIcon: const Icon(Icons.arrow_drop_up_rounded), + onSelected: (value) { + selectedCountry.value = value.toString(); + onSelected({ + 'country': selectedCountry.value, + 'state': selectedState.value, + 'city': selectedCity.value, + }); + }, + ), + const SizedBox( + height: 16, + ), + DropdownMenu( + dropdownMenuEntries: switch (states) { + AsyncError() => [], + AsyncData(:final value) => value + .map( + (e) => DropdownMenuEntry( + value: e, + label: e, + ), + ) + .toList(), + _ => [], + }, + menuHeight: 400, + width: context.width * 0.9, + label: const Text('State'), + inputDecorationTheme: inputDecorationTheme, + menuStyle: menuStyle, + controller: stateTextController, + trailingIcon: const Icon(Icons.arrow_drop_down_rounded), + selectedTrailingIcon: const Icon(Icons.arrow_drop_up_rounded), + onSelected: (value) { + selectedState.value = value.toString(); + onSelected({ + 'country': selectedCountry.value, + 'state': selectedState.value, + 'city': selectedCity.value, + }); + }, + ), + const SizedBox( + height: 16, + ), + DropdownMenu( + dropdownMenuEntries: switch (cities) { + AsyncError() => [], + AsyncData(:final value) => value + .map( + (e) => DropdownMenuEntry( + value: e, + label: e, + ), + ) + .toList(), + _ => [], + }, + menuHeight: 400, + width: context.width * 0.9, + label: const Text('City'), + inputDecorationTheme: inputDecorationTheme, + menuStyle: menuStyle, + controller: cityTextController, + trailingIcon: const Icon(Icons.arrow_drop_down_rounded), + selectedTrailingIcon: const Icon(Icons.arrow_drop_up_rounded), + onSelected: (value) { + selectedCity.value = value.toString(); + onSelected({ + 'country': selectedCountry.value, + 'state': selectedState.value, + 'city': selectedCity.value, + }); + }, + ), + ], + ); + } +} diff --git a/mobile/lib/modules/search/ui/search_filter/media_type_picker.dart b/mobile/lib/modules/search/ui/search_filter/media_type_picker.dart new file mode 100644 index 000000000..aaef2c815 --- /dev/null +++ b/mobile/lib/modules/search/ui/search_filter/media_type_picker.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; + +class MediaTypePicker extends HookWidget { + const MediaTypePicker({super.key, required this.onSelect, this.filter}); + + final Function(AssetType) onSelect; + final AssetType? filter; + + @override + Widget build(BuildContext context) { + final selectedMediaType = useState(filter ?? AssetType.other); + + return ListView( + shrinkWrap: true, + children: [ + RadioListTile( + title: const Text("All"), + value: AssetType.other, + onChanged: (value) { + selectedMediaType.value = value!; + onSelect(value); + }, + groupValue: selectedMediaType.value, + ), + RadioListTile( + title: const Text("Image"), + value: AssetType.image, + onChanged: (value) { + selectedMediaType.value = value!; + onSelect(value); + }, + groupValue: selectedMediaType.value, + ), + RadioListTile( + title: const Text("Video"), + value: AssetType.video, + onChanged: (value) { + selectedMediaType.value = value!; + onSelect(value); + }, + groupValue: selectedMediaType.value, + ), + ], + ); + } +} diff --git a/mobile/lib/modules/search/ui/search_filter/people_picker.dart b/mobile/lib/modules/search/ui/search_filter/people_picker.dart new file mode 100644 index 000000000..74aad06b8 --- /dev/null +++ b/mobile/lib/modules/search/ui/search_filter/people_picker.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/modules/search/providers/people.provider.dart'; +import 'package:immich_mobile/shared/models/store.dart' as local_store; +import 'package:immich_mobile/utils/image_url_builder.dart'; +import 'package:openapi/api.dart'; + +class PeoplePicker extends HookConsumerWidget { + const PeoplePicker({super.key, required this.onSelect, this.filter}); + + final Function(Set) onSelect; + final Set? filter; + + @override + Widget build(BuildContext context, WidgetRef ref) { + var imageSize = 45.0; + final people = ref.watch(getAllPeopleProvider); + final headers = { + "x-immich-user-token": + local_store.Store.get(local_store.StoreKey.accessToken), + }; + final selectedPeople = useState>(filter ?? {}); + + return people.widgetWhen( + onData: (people) { + return ListView.builder( + shrinkWrap: true, + itemCount: people.length, + padding: const EdgeInsets.all(8), + itemBuilder: (context, index) { + final person = people[index]; + return Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(15)), + ), + child: ListTile( + title: Text( + person.name, + style: context.textTheme.bodyLarge, + ), + leading: SizedBox( + height: imageSize, + child: Material( + shape: const CircleBorder(side: BorderSide.none), + elevation: 3, + child: CircleAvatar( + maxRadius: imageSize / 2, + backgroundImage: NetworkImage( + getFaceThumbnailUrl(person.id), + headers: headers, + ), + ), + ), + ), + onTap: () { + if (selectedPeople.value.contains(person)) { + selectedPeople.value.remove(person); + } else { + selectedPeople.value.add(person); + } + + selectedPeople.value = {...selectedPeople.value}; + onSelect(selectedPeople.value); + }, + selected: selectedPeople.value.contains(person), + selectedTileColor: context.primaryColor.withOpacity(0.2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(15)), + ), + ), + ); + }, + ); + }, + ); + } +} diff --git a/mobile/lib/modules/search/ui/search_filter/search_filter_chip.dart b/mobile/lib/modules/search/ui/search_filter/search_filter_chip.dart new file mode 100644 index 000000000..b2e0d086a --- /dev/null +++ b/mobile/lib/modules/search/ui/search_filter/search_filter_chip.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class SearchFilterChip extends StatelessWidget { + final String label; + final Function() onTap; + final Widget? currentFilter; + final IconData icon; + + const SearchFilterChip({ + super.key, + required this.label, + required this.onTap, + required this.icon, + this.currentFilter, + }); + + @override + Widget build(BuildContext context) { + if (currentFilter != null) { + return GestureDetector( + onTap: onTap, + child: Card( + elevation: 0, + color: context.primaryColor.withAlpha(25), + shape: StadiumBorder( + side: BorderSide(color: context.primaryColor), + ), + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), + child: Row( + children: [ + Icon( + icon, + size: 18, + ), + const SizedBox(width: 4.0), + currentFilter!, + ], + ), + ), + ), + ); + } + return GestureDetector( + onTap: onTap, + child: Card( + elevation: 0, + shape: + StadiumBorder(side: BorderSide(color: Colors.grey.withAlpha(100))), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), + child: Row( + children: [ + Icon( + icon, + size: 18, + ), + const SizedBox(width: 4.0), + Text(label), + ], + ), + ), + ), + ); + } +} diff --git a/mobile/lib/modules/search/ui/search_filter/search_filter_utils.dart b/mobile/lib/modules/search/ui/search_filter/search_filter_utils.dart new file mode 100644 index 000000000..57545413d --- /dev/null +++ b/mobile/lib/modules/search/ui/search_filter/search_filter_utils.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +Future showFilterBottomSheet({ + required BuildContext context, + required Widget child, + bool isScrollControlled = false, + bool isDismissible = true, +}) async { + return await showModalBottomSheet( + context: context, + isScrollControlled: isScrollControlled, + useSafeArea: false, + isDismissible: isDismissible, + showDragHandle: isDismissible, + builder: (BuildContext context) { + return child; + }, + ); +} diff --git a/mobile/lib/modules/search/ui/search_suggestion_list.dart b/mobile/lib/modules/search/ui/search_suggestion_list.dart deleted file mode 100644 index c9694eb75..000000000 --- a/mobile/lib/modules/search/ui/search_suggestion_list.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; - -class SearchSuggestionList extends ConsumerWidget { - const SearchSuggestionList({super.key, required this.onSubmitted}); - - final Function(String) onSubmitted; - @override - Widget build(BuildContext context, WidgetRef ref) { - final searchTerm = ref.watch(searchPageStateProvider).searchTerm; - final searchSuggestion = - ref.watch(searchPageStateProvider).searchSuggestion; - - return Container( - color: searchTerm.isEmpty - ? Colors.black.withOpacity(0.5) - : context.scaffoldBackgroundColor, - child: CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: Container( - color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[100], - child: Padding( - padding: const EdgeInsets.all(16.0), - child: RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'search_suggestion_list_smart_search_hint_1'.tr(), - style: context.textTheme.bodyMedium, - ), - TextSpan( - text: 'search_suggestion_list_smart_search_hint_2'.tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ), - ), - ), - SliverFillRemaining( - hasScrollBody: true, - child: ListView.builder( - itemBuilder: ((context, index) { - return ListTile( - onTap: () { - onSubmitted("m:${searchSuggestion[index]}"); - }, - title: Text(searchSuggestion[index]), - ); - }), - itemCount: searchSuggestion.length, - ), - ), - ], - ), - ); - } -} diff --git a/mobile/lib/modules/search/views/all_people_page.dart b/mobile/lib/modules/search/views/all_people_page.dart index 1f90922c1..3414edc05 100644 --- a/mobile/lib/modules/search/views/all_people_page.dart +++ b/mobile/lib/modules/search/views/all_people_page.dart @@ -1,35 +1,38 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; -import 'package:immich_mobile/modules/search/providers/people.provider.dart'; -import 'package:immich_mobile/modules/search/ui/explore_grid.dart'; - -@RoutePage() -class AllPeoplePage extends HookConsumerWidget { - const AllPeoplePage({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final curatedPeople = ref.watch(getCuratedPeopleProvider); - - return Scaffold( - appBar: AppBar( - title: const Text( - 'all_people_page_title', - ).tr(), - leading: IconButton( - onPressed: () => context.popRoute(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: curatedPeople.widgetWhen( - onData: (people) => ExploreGrid( - isPeople: true, - curatedContent: people, - ), - ), - ); - } -} +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/modules/search/models/curated_content.dart'; +import 'package:immich_mobile/modules/search/providers/people.provider.dart'; +import 'package:immich_mobile/modules/search/ui/explore_grid.dart'; + +@RoutePage() +class AllPeoplePage extends HookConsumerWidget { + const AllPeoplePage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final curatedPeople = ref.watch(getAllPeopleProvider); + + return Scaffold( + appBar: AppBar( + title: const Text( + 'all_people_page_title', + ).tr(), + leading: IconButton( + onPressed: () => context.popRoute(), + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + ), + body: curatedPeople.widgetWhen( + onData: (people) => ExploreGrid( + isPeople: true, + curatedContent: people + .map((e) => CuratedContent(label: e.name, id: e.id)) + .toList(), + ), + ), + ); + } +} diff --git a/mobile/lib/modules/search/views/search_input_page.dart b/mobile/lib/modules/search/views/search_input_page.dart new file mode 100644 index 000000000..a35341606 --- /dev/null +++ b/mobile/lib/modules/search/views/search_input_page.dart @@ -0,0 +1,563 @@ +import 'dart:async'; + +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; +import 'package:immich_mobile/modules/search/providers/paginated_search.provider.dart'; +import 'package:immich_mobile/modules/search/ui/search_filter/camera_picker.dart'; +import 'package:immich_mobile/modules/search/ui/search_filter/display_option_picker.dart'; +import 'package:immich_mobile/modules/search/ui/search_filter/filter_bottom_sheet_scaffold.dart'; +import 'package:immich_mobile/modules/search/ui/search_filter/location_picker.dart'; +import 'package:immich_mobile/modules/search/ui/search_filter/media_type_picker.dart'; +import 'package:immich_mobile/modules/search/ui/search_filter/people_picker.dart'; +import 'package:immich_mobile/modules/search/ui/search_filter/search_filter_chip.dart'; +import 'package:immich_mobile/modules/search/ui/search_filter/search_filter_utils.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/ui/asset_grid/multiselect_grid.dart'; +import 'package:openapi/api.dart'; + +@RoutePage() +class SearchInputPage extends HookConsumerWidget { + const SearchInputPage({super.key, this.prefilter}); + + final SearchFilter? prefilter; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isContextualSearch = useState(true); + final textSearchController = useTextEditingController(); + final filter = useState( + SearchFilter( + people: prefilter?.people ?? {}, + location: prefilter?.location ?? SearchLocationFilter(), + camera: prefilter?.camera ?? SearchCameraFilter(), + date: prefilter?.date ?? SearchDateFilter(), + display: prefilter?.display ?? + SearchDisplayFilters( + isNotInAlbum: false, + isArchive: false, + isFavorite: false, + ), + mediaType: prefilter?.mediaType ?? AssetType.other, + ), + ); + + final previousFilter = useState(filter.value); + + final peopleCurrentFilterWidget = useState(null); + final dateRangeCurrentFilterWidget = useState(null); + final cameraCurrentFilterWidget = useState(null); + final locationCurrentFilterWidget = useState(null); + final mediaTypeCurrentFilterWidget = useState(null); + final displayOptionCurrentFilterWidget = useState(null); + + final currentPage = useState(1); + final searchProvider = ref.watch(paginatedSearchProvider); + final searchResultCount = useState(0); + + search() async { + if (prefilter == null && filter.value == previousFilter.value) return; + + ref.watch(paginatedSearchProvider.notifier).clear(); + + currentPage.value = 1; + + final searchResult = await ref + .watch(paginatedSearchProvider.notifier) + .getNextPage(filter.value, currentPage.value); + previousFilter.value = filter.value; + + searchResultCount.value = searchResult.length; + } + + searchPrefilter() { + if (prefilter != null) { + Future.delayed( + Duration.zero, + () { + search(); + + if (prefilter!.location.city != null) { + locationCurrentFilterWidget.value = Text( + prefilter!.location.city!, + style: context.textTheme.labelLarge, + ); + } + }, + ); + } + } + + useEffect( + () { + searchPrefilter(); + return null; + }, + [], + ); + + loadMoreSearchResult() async { + currentPage.value += 1; + final searchResult = await ref + .watch(paginatedSearchProvider.notifier) + .getNextPage(filter.value, currentPage.value); + searchResultCount.value = searchResult.length; + } + + showPeoplePicker() { + handleOnSelect(Set value) { + filter.value = filter.value.copyWith( + people: value, + ); + + peopleCurrentFilterWidget.value = Text( + value.map((e) => e.name != '' ? e.name : "No name").join(', '), + style: context.textTheme.labelLarge, + ); + } + + handleClear() { + filter.value = filter.value.copyWith( + people: {}, + ); + + peopleCurrentFilterWidget.value = null; + search(); + } + + showFilterBottomSheet( + context: context, + isScrollControlled: true, + child: FractionallySizedBox( + heightFactor: 0.8, + child: FilterBottomSheetScaffold( + title: 'Select people', + expanded: true, + onSearch: search, + onClear: handleClear, + child: PeoplePicker( + onSelect: handleOnSelect, + filter: filter.value.people, + ), + ), + ), + ); + } + + showLocationPicker() { + handleOnSelect(Map value) { + filter.value = filter.value.copyWith( + location: SearchLocationFilter( + country: value['country'], + city: value['city'], + state: value['state'], + ), + ); + + final locationText = []; + if (value['country'] != null) { + locationText.add(value['country']!); + } + + if (value['state'] != null) { + locationText.add(value['state']!); + } + + if (value['city'] != null) { + locationText.add(value['city']!); + } + + locationCurrentFilterWidget.value = Text( + locationText.join(', '), + style: context.textTheme.labelLarge, + ); + } + + handleClear() { + filter.value = filter.value.copyWith( + location: SearchLocationFilter(), + ); + + locationCurrentFilterWidget.value = null; + search(); + } + + showFilterBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + child: FilterBottomSheetScaffold( + title: 'Select location', + onSearch: search, + onClear: handleClear, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Container( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: LocationPicker( + onSelected: handleOnSelect, + filter: filter.value.location, + ), + ), + ), + ), + ); + } + + showCameraPicker() { + handleOnSelect(Map value) { + filter.value = filter.value.copyWith( + camera: SearchCameraFilter( + make: value['make'], + model: value['model'], + ), + ); + + cameraCurrentFilterWidget.value = Text( + '${value['make'] ?? ''} ${value['model'] ?? ''}', + style: context.textTheme.labelLarge, + ); + } + + handleClear() { + filter.value = filter.value.copyWith( + camera: SearchCameraFilter(), + ); + + cameraCurrentFilterWidget.value = null; + search(); + } + + showFilterBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + child: FilterBottomSheetScaffold( + title: 'Select camera type', + onSearch: search, + onClear: handleClear, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: CameraPicker( + onSelect: handleOnSelect, + filter: filter.value.camera, + ), + ), + ), + ); + } + + showDatePicker() async { + final firstDate = DateTime(1900); + final lastDate = DateTime.now(); + + final date = await showDateRangePicker( + context: context, + firstDate: firstDate, + lastDate: lastDate, + currentDate: DateTime.now(), + initialDateRange: DateTimeRange( + start: filter.value.date.takenAfter ?? lastDate, + end: filter.value.date.takenBefore ?? lastDate, + ), + helpText: 'Select a date range', + cancelText: 'Cancel', + confirmText: 'Select', + saveText: 'Save', + errorFormatText: 'Invalid date format', + errorInvalidText: 'Invalid date', + fieldStartHintText: 'Start date', + fieldEndHintText: 'End date', + initialEntryMode: DatePickerEntryMode.input, + ); + + if (date == null) { + filter.value = filter.value.copyWith( + date: SearchDateFilter(), + ); + + dateRangeCurrentFilterWidget.value = null; + search(); + return; + } + + filter.value = filter.value.copyWith( + date: SearchDateFilter( + takenAfter: date.start, + takenBefore: date.end.add( + const Duration( + hours: 23, + minutes: 59, + seconds: 59, + ), + ), + ), + ); + + // If date range is less than 24 hours, set the end date to the end of the day + if (date.end.difference(date.start).inHours < 24) { + dateRangeCurrentFilterWidget.value = Text( + date.start.toLocal().toIso8601String().split('T').first, + style: context.textTheme.labelLarge, + ); + } else { + dateRangeCurrentFilterWidget.value = Text( + '${date.start.toLocal().toIso8601String().split('T').first} to ${date.end.toLocal().toIso8601String().split('T').first}', + style: context.textTheme.labelLarge, + ); + } + + search(); + } + + // MEDIA PICKER + showMediaTypePicker() { + handleOnSelected(AssetType assetType) { + filter.value = filter.value.copyWith( + mediaType: assetType, + ); + + mediaTypeCurrentFilterWidget.value = Text( + assetType == AssetType.image ? 'Image' : 'Video', + style: context.textTheme.labelLarge, + ); + } + + handleClear() { + filter.value = filter.value.copyWith( + mediaType: AssetType.other, + ); + + mediaTypeCurrentFilterWidget.value = null; + search(); + } + + showFilterBottomSheet( + context: context, + child: FilterBottomSheetScaffold( + title: 'Select media type', + onSearch: search, + onClear: handleClear, + child: MediaTypePicker( + onSelect: handleOnSelected, + filter: filter.value.mediaType, + ), + ), + ); + } + + // DISPLAY OPTION + showDisplayOptionPicker() { + handleOnSelect(Map value) { + final filterText = []; + + value.forEach((key, value) { + switch (key) { + case DisplayOption.notInAlbum: + filter.value = filter.value.copyWith( + display: filter.value.display.copyWith( + isNotInAlbum: value, + ), + ); + if (value) filterText.add('Not in album'); + break; + case DisplayOption.archive: + filter.value = filter.value.copyWith( + display: filter.value.display.copyWith( + isArchive: value, + ), + ); + if (value) filterText.add('Archive'); + break; + case DisplayOption.favorite: + filter.value = filter.value.copyWith( + display: filter.value.display.copyWith( + isFavorite: value, + ), + ); + if (value) filterText.add('Favorite'); + break; + } + }); + + displayOptionCurrentFilterWidget.value = Text( + filterText.join(', '), + style: context.textTheme.labelLarge, + ); + } + + handleClear() { + filter.value = filter.value.copyWith( + display: SearchDisplayFilters( + isNotInAlbum: false, + isArchive: false, + isFavorite: false, + ), + ); + + displayOptionCurrentFilterWidget.value = null; + search(); + } + + showFilterBottomSheet( + context: context, + child: FilterBottomSheetScaffold( + title: 'Display options', + onSearch: search, + onClear: handleClear, + child: DisplayOptionPicker( + onSelect: handleOnSelect, + filter: filter.value.display, + ), + ), + ); + } + + handleTextSubmitted(String value) { + if (isContextualSearch.value) { + filter.value = filter.value.copyWith( + context: value, + filename: null, + ); + } else { + filter.value = filter.value.copyWith(filename: value, context: null); + } + + search(); + } + + buildSearchResult() { + return switch (searchProvider) { + AsyncData() => Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: NotificationListener( + onNotification: (notification) { + final metrics = notification.metrics; + final shouldLoadMore = searchResultCount.value > 75; + if (metrics.pixels >= metrics.maxScrollExtent && + shouldLoadMore) { + loadMoreSearchResult(); + } + return true; + }, + child: MultiselectGrid( + renderListProvider: paginatedSearchRenderListProvider, + archiveEnabled: true, + deleteEnabled: true, + editEnabled: true, + favoriteEnabled: true, + stackEnabled: false, + emptyIndicator: const SizedBox(), + ), + ), + ), + ), + AsyncError(:final error) => Text('Error: $error'), + _ => const Expanded(child: Center(child: CircularProgressIndicator())), + }; + } + + return Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar( + automaticallyImplyLeading: true, + actions: [ + IconButton( + icon: isContextualSearch.value + ? const Icon(Icons.abc_rounded) + : const Icon(Icons.image_search_rounded), + onPressed: () { + isContextualSearch.value = !isContextualSearch.value; + textSearchController.clear(); + }, + ), + ], + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios_new_rounded), + onPressed: () { + context.router.pop(); + }, + ), + title: TextField( + controller: textSearchController, + decoration: InputDecoration( + hintText: isContextualSearch.value + ? 'Sunrise on the beach' + : 'File name or extension', + hintStyle: context.textTheme.bodyLarge?.copyWith( + color: context.themeData.colorScheme.onSurface.withOpacity(0.75), + fontWeight: FontWeight.w500, + ), + enabledBorder: const UnderlineInputBorder( + borderSide: BorderSide(color: Colors.transparent), + ), + focusedBorder: const UnderlineInputBorder( + borderSide: BorderSide(color: Colors.transparent), + ), + ), + onSubmitted: handleTextSubmitted, + ), + ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 12.0), + child: SizedBox( + height: 50, + child: ListView( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 16), + children: [ + SearchFilterChip( + icon: Icons.people_alt_rounded, + onTap: showPeoplePicker, + label: 'People', + currentFilter: peopleCurrentFilterWidget.value, + ), + SearchFilterChip( + icon: Icons.location_pin, + onTap: showLocationPicker, + label: 'Location', + currentFilter: locationCurrentFilterWidget.value, + ), + SearchFilterChip( + icon: Icons.camera_alt_rounded, + onTap: showCameraPicker, + label: 'Camera', + currentFilter: cameraCurrentFilterWidget.value, + ), + SearchFilterChip( + icon: Icons.date_range_rounded, + onTap: showDatePicker, + label: 'Date', + currentFilter: dateRangeCurrentFilterWidget.value, + ), + SearchFilterChip( + icon: Icons.video_collection_outlined, + onTap: showMediaTypePicker, + label: 'Media Type', + currentFilter: mediaTypeCurrentFilterWidget.value, + ), + SearchFilterChip( + icon: Icons.display_settings_outlined, + onTap: showDisplayOptionPicker, + label: 'Display Options', + currentFilter: displayOptionCurrentFilterWidget.value, + ), + ], + ), + ), + ), + buildSearchResult(), + ], + ), + ); + } +} diff --git a/mobile/lib/modules/search/views/search_page.dart b/mobile/lib/modules/search/views/search_page.dart index ab114d691..27ca28126 100644 --- a/mobile/lib/modules/search/views/search_page.dart +++ b/mobile/lib/modules/search/views/search_page.dart @@ -1,279 +1,274 @@ -import 'dart:math' as math; -import 'package:auto_route/auto_route.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart' hide Store; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/modules/search/models/curated_content.dart'; -import 'package:immich_mobile/modules/search/providers/people.provider.dart'; -import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; -import 'package:immich_mobile/modules/search/ui/curated_people_row.dart'; -import 'package:immich_mobile/modules/search/ui/curated_places_row.dart'; -import 'package:immich_mobile/modules/search/ui/immich_search_bar.dart'; -import 'package:immich_mobile/modules/search/ui/person_name_edit_form.dart'; -import 'package:immich_mobile/modules/search/ui/search_row_title.dart'; -import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart'; -import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/shared/providers/server_info.provider.dart'; -import 'package:immich_mobile/shared/ui/scaffold_error_body.dart'; - -@RoutePage() -// ignore: must_be_immutable -class SearchPage extends HookConsumerWidget { - SearchPage({super.key}); - - FocusNode searchFocusNode = FocusNode(); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled; - final curatedLocation = ref.watch(getCuratedLocationProvider); - final curatedPeople = ref.watch(getCuratedPeopleProvider); - final isMapEnabled = - ref.watch(serverInfoProvider.select((v) => v.serverFeatures.map)); - double imageSize = math.min(context.width / 3, 150); - - TextStyle categoryTitleStyle = const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 15.0, - ); - - Color categoryIconColor = context.isDarkTheme ? Colors.white : Colors.black; - - useEffect( - () { - searchFocusNode = FocusNode(); - return () => searchFocusNode.dispose(); - }, - [], - ); - - onSearchSubmitted(String searchTerm) async { - searchFocusNode.unfocus(); - ref.watch(searchPageStateProvider.notifier).disableSearch(); - - context.pushRoute( - SearchResultRoute( - searchTerm: searchTerm, - ), - ); - } - - showNameEditModel( - String personId, - String personName, - ) { - return showDialog( - context: context, - builder: (BuildContext context) { - return PersonNameEditForm(personId: personId, personName: personName); - }, - ); - } - - buildPeople() { - return SizedBox( - height: imageSize, - child: curatedPeople.widgetWhen( - onError: (error, stack) => const ScaffoldErrorBody(withIcon: false), - onData: (people) => Padding( - padding: const EdgeInsets.only( - left: 16, - top: 8, - ), - child: CuratedPeopleRow( - content: people.take(12).toList(), - onTap: (content, index) { - context.pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ); - }, - onNameTap: (person, index) => { - showNameEditModel(person.id, person.label), - }, - ), - ), - ), - ); - } - - buildPlaces() { - return SizedBox( - height: imageSize, - child: curatedLocation.widgetWhen( - onError: (error, stack) => const ScaffoldErrorBody(withIcon: false), - onData: (locations) => CuratedPlacesRow( - isMapEnabled: isMapEnabled, - content: locations - .map( - (o) => CuratedContent( - id: o.id, - label: o.city, - ), - ) - .toList(), - imageSize: imageSize, - onTap: (content, index) { - context.pushRoute( - SearchResultRoute( - searchTerm: 'm:${content.label}', - ), - ); - }, - ), - ), - ); - } - - return Scaffold( - appBar: ImmichSearchBar( - searchFocusNode: searchFocusNode, - onSubmitted: onSearchSubmitted, - ), - body: GestureDetector( - onTap: () { - searchFocusNode.unfocus(); - ref.watch(searchPageStateProvider.notifier).disableSearch(); - }, - child: Stack( - children: [ - ListView( - children: [ - SearchRowTitle( - title: "search_page_people".tr(), - onViewAllPressed: () => - context.pushRoute(const AllPeopleRoute()), - ), - buildPeople(), - SearchRowTitle( - title: "search_page_places".tr(), - onViewAllPressed: () => - context.pushRoute(const CuratedLocationRoute()), - top: 0, - ), - const SizedBox(height: 10.0), - buildPlaces(), - const SizedBox(height: 24.0), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - 'search_page_your_activity', - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ).tr(), - ), - ListTile( - leading: Icon( - Icons.favorite_border_rounded, - color: categoryIconColor, - ), - title: - Text('search_page_favorites', style: categoryTitleStyle) - .tr(), - onTap: () => context.pushRoute(const FavoritesRoute()), - ), - const CategoryDivider(), - ListTile( - leading: Icon( - Icons.schedule_outlined, - color: categoryIconColor, - ), - title: Text( - 'search_page_recently_added', - style: categoryTitleStyle, - ).tr(), - onTap: () => context.pushRoute(const RecentlyAddedRoute()), - ), - const SizedBox(height: 24.0), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - 'search_page_categories', - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ).tr(), - ), - ListTile( - title: - Text('search_page_screenshots', style: categoryTitleStyle) - .tr(), - leading: Icon( - Icons.screenshot, - color: categoryIconColor, - ), - onTap: () => context.pushRoute( - SearchResultRoute( - searchTerm: 'screenshots', - ), - ), - ), - const CategoryDivider(), - ListTile( - title: Text('search_page_selfies', style: categoryTitleStyle) - .tr(), - leading: Icon( - Icons.photo_camera_front_outlined, - color: categoryIconColor, - ), - onTap: () => context.pushRoute( - SearchResultRoute( - searchTerm: 'selfies', - ), - ), - ), - const CategoryDivider(), - ListTile( - title: Text('search_page_videos', style: categoryTitleStyle) - .tr(), - leading: Icon( - Icons.play_circle_outline, - color: categoryIconColor, - ), - onTap: () => context.pushRoute(const AllVideosRoute()), - ), - const CategoryDivider(), - ListTile( - title: Text( - 'search_page_motion_photos', - style: categoryTitleStyle, - ).tr(), - leading: Icon( - Icons.motion_photos_on_outlined, - color: categoryIconColor, - ), - onTap: () => context.pushRoute(const AllMotionPhotosRoute()), - ), - ], - ), - if (isSearchEnabled) - SearchSuggestionList(onSubmitted: onSearchSubmitted), - ], - ), - ), - ); - } -} - -class CategoryDivider extends StatelessWidget { - const CategoryDivider({super.key}); - - @override - Widget build(BuildContext context) { - return const Padding( - padding: EdgeInsets.only( - left: 56, - right: 16, - ), - child: Divider( - height: 0, - ), - ); - } -} +import 'dart:math' as math; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/modules/search/models/curated_content.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; +import 'package:immich_mobile/modules/search/providers/people.provider.dart'; +import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; +import 'package:immich_mobile/modules/search/ui/curated_people_row.dart'; +import 'package:immich_mobile/modules/search/ui/curated_places_row.dart'; +import 'package:immich_mobile/modules/search/ui/person_name_edit_form.dart'; +import 'package:immich_mobile/modules/search/ui/search_row_title.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/providers/server_info.provider.dart'; +import 'package:immich_mobile/shared/ui/immich_app_bar.dart'; +import 'package:immich_mobile/shared/ui/scaffold_error_body.dart'; + +@RoutePage() +// ignore: must_be_immutable +class SearchPage extends HookConsumerWidget { + const SearchPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final curatedLocation = ref.watch(getCuratedLocationProvider); + final curatedPeople = ref.watch(getAllPeopleProvider); + final isMapEnabled = + ref.watch(serverInfoProvider.select((v) => v.serverFeatures.map)); + double imageSize = math.min(context.width / 3, 150); + + TextStyle categoryTitleStyle = const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 15.0, + ); + + Color categoryIconColor = context.isDarkTheme ? Colors.white : Colors.black; + + showNameEditModel( + String personId, + String personName, + ) { + return showDialog( + context: context, + builder: (BuildContext context) { + return PersonNameEditForm(personId: personId, personName: personName); + }, + ); + } + + buildPeople() { + return SizedBox( + height: imageSize, + child: curatedPeople.widgetWhen( + onError: (error, stack) => const ScaffoldErrorBody(withIcon: false), + onData: (people) => Padding( + padding: const EdgeInsets.only( + left: 16, + top: 8, + ), + child: CuratedPeopleRow( + content: people + .map((e) => CuratedContent(label: e.name, id: e.id)) + .take(12) + .toList(), + onTap: (content, index) { + context.pushRoute( + PersonResultRoute( + personId: content.id, + personName: content.label, + ), + ); + }, + onNameTap: (person, index) => { + showNameEditModel(person.id, person.label), + }, + ), + ), + ), + ); + } + + buildPlaces() { + return SizedBox( + height: imageSize, + child: curatedLocation.widgetWhen( + onError: (error, stack) => const ScaffoldErrorBody(withIcon: false), + onData: (locations) => CuratedPlacesRow( + isMapEnabled: isMapEnabled, + content: locations + .map( + (o) => CuratedContent( + id: o.id, + label: o.city, + ), + ) + .toList(), + imageSize: imageSize, + onTap: (content, index) { + context.pushRoute( + SearchInputRoute( + prefilter: SearchFilter( + people: {}, + location: SearchLocationFilter( + city: content.label, + ), + camera: SearchCameraFilter(), + date: SearchDateFilter(), + display: SearchDisplayFilters( + isNotInAlbum: false, + isArchive: false, + isFavorite: false, + ), + mediaType: AssetType.other, + ), + ), + ); + }, + ), + ), + ); + } + + buildSearchButton() { + return GestureDetector( + onTap: () { + context.pushRoute(SearchInputRoute()); + }, + child: Card( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + side: BorderSide( + color: context.isDarkTheme + ? Colors.grey[800]! + : const Color.fromARGB(255, 225, 225, 225), + ), + ), + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 12.0, + ), + child: Row( + children: [ + Icon(Icons.search, color: context.primaryColor), + const SizedBox(width: 16.0), + Text( + "Search your photos", + style: context.textTheme.bodyLarge?.copyWith( + color: + context.isDarkTheme ? Colors.white70 : Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ), + ); + } + + return Scaffold( + appBar: const ImmichAppBar(), + body: Stack( + children: [ + ListView( + children: [ + buildSearchButton(), + SearchRowTitle( + title: "search_page_people".tr(), + onViewAllPressed: () => + context.pushRoute(const AllPeopleRoute()), + ), + buildPeople(), + SearchRowTitle( + title: "search_page_places".tr(), + onViewAllPressed: () => + context.pushRoute(const CuratedLocationRoute()), + top: 0, + ), + const SizedBox(height: 10.0), + buildPlaces(), + const SizedBox(height: 24.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'search_page_your_activity', + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + ), + ).tr(), + ), + ListTile( + leading: Icon( + Icons.favorite_border_rounded, + color: categoryIconColor, + ), + title: Text('search_page_favorites', style: categoryTitleStyle) + .tr(), + onTap: () => context.pushRoute(const FavoritesRoute()), + ), + const CategoryDivider(), + ListTile( + leading: Icon( + Icons.schedule_outlined, + color: categoryIconColor, + ), + title: Text( + 'search_page_recently_added', + style: categoryTitleStyle, + ).tr(), + onTap: () => context.pushRoute(const RecentlyAddedRoute()), + ), + const SizedBox(height: 24.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + 'search_page_categories', + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + ), + ).tr(), + ), + ListTile( + title: + Text('search_page_videos', style: categoryTitleStyle).tr(), + leading: Icon( + Icons.play_circle_outline, + color: categoryIconColor, + ), + onTap: () => context.pushRoute(const AllVideosRoute()), + ), + const CategoryDivider(), + ListTile( + title: Text( + 'search_page_motion_photos', + style: categoryTitleStyle, + ).tr(), + leading: Icon( + Icons.motion_photos_on_outlined, + color: categoryIconColor, + ), + onTap: () => context.pushRoute(const AllMotionPhotosRoute()), + ), + ], + ), + ], + ), + ); + } +} + +class CategoryDivider extends StatelessWidget { + const CategoryDivider({super.key}); + + @override + Widget build(BuildContext context) { + return const Padding( + padding: EdgeInsets.only( + left: 56, + right: 16, + ), + child: Divider( + height: 0, + ), + ); + } +} diff --git a/mobile/lib/modules/search/views/search_result_page.dart b/mobile/lib/modules/search/views/search_result_page.dart deleted file mode 100644 index 97df5f10c..000000000 --- a/mobile/lib/modules/search/views/search_result_page.dart +++ /dev/null @@ -1,213 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; -import 'package:immich_mobile/modules/search/providers/search_result_page.provider.dart'; -import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart'; -import 'package:immich_mobile/shared/ui/asset_grid/multiselect_grid.dart'; -import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; - -class SearchType { - SearchType({required this.isSmart, required this.searchTerm}); - - final bool isSmart; - final String searchTerm; -} - -SearchType _getSearchType(String searchTerm) { - if (searchTerm.startsWith('m:')) { - return SearchType(isSmart: false, searchTerm: searchTerm.substring(2)); - } else { - return SearchType(isSmart: true, searchTerm: searchTerm); - } -} - -@RoutePage() -class SearchResultPage extends HookConsumerWidget { - const SearchResultPage({ - super.key, - required this.searchTerm, - }); - - final String searchTerm; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final searchTermController = useTextEditingController(text: ""); - final isNewSearch = useState(false); - final currentSearchTerm = useState(searchTerm); - - FocusNode? searchFocusNode; - - useEffect( - () { - searchFocusNode = FocusNode(); - - var searchType = _getSearchType(searchTerm); - - Future.delayed( - Duration.zero, - () => ref - .read(searchResultPageProvider.notifier) - .search(searchType.searchTerm, smartSearch: searchType.isSmart), - ); - return () => searchFocusNode?.dispose(); - }, - [], - ); - - Future onSearchSubmitted(String newSearchTerm) { - debugPrint("Re-Search with $newSearchTerm"); - searchFocusNode?.unfocus(); - isNewSearch.value = false; - currentSearchTerm.value = newSearchTerm; - var searchType = _getSearchType(newSearchTerm); - return ref - .watch(searchResultPageProvider.notifier) - .search(searchType.searchTerm, smartSearch: searchType.isSmart); - } - - buildTextField() { - return TextField( - controller: searchTermController, - focusNode: searchFocusNode, - autofocus: false, - onTap: () { - searchTermController.clear(); - ref.watch(searchPageStateProvider.notifier).setSearchTerm(""); - searchFocusNode?.requestFocus(); - }, - textInputAction: TextInputAction.search, - onSubmitted: (searchTerm) { - if (searchTerm.isNotEmpty) { - searchTermController.clear(); - onSearchSubmitted(searchTerm); - } else { - isNewSearch.value = false; - } - }, - onChanged: (value) { - ref.watch(searchPageStateProvider.notifier).setSearchTerm(value); - }, - decoration: InputDecoration( - hintText: 'search_result_page_new_search_hint'.tr(), - enabledBorder: const UnderlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const UnderlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - hintStyle: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16.0, - color: context.isDarkTheme - ? Colors.grey[500] - : Colors.black.withOpacity(0.5), - ), - ), - ); - } - - buildChip() { - return Chip( - label: Wrap( - spacing: 5, - runAlignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - alignment: WrapAlignment.center, - children: [ - Text( - currentSearchTerm.value, - style: TextStyle( - color: context.primaryColor, - fontSize: 13, - fontWeight: FontWeight.bold, - ), - maxLines: 1, - ), - Icon( - Icons.close_rounded, - color: context.primaryColor, - size: 20, - ), - ], - ), - backgroundColor: context.primaryColor.withAlpha(50), - ); - } - - Future refresh() async => onSearchSubmitted(currentSearchTerm.value); - - buildSearchResult() { - final searchResultPageState = ref.watch(searchResultPageProvider); - - if (searchResultPageState.isError) { - return Padding( - padding: const EdgeInsets.all(12), - child: const Text("common_server_error").tr(), - ); - } - - if (searchResultPageState.isLoading) { - return const Center(child: ImmichLoadingIndicator()); - } - - if (searchResultPageState.isSuccess) { - return MultiselectGrid( - renderListProvider: searchRenderListProvider, - archiveEnabled: true, - deleteEnabled: true, - editEnabled: true, - favoriteEnabled: true, - stackEnabled: false, - onRefresh: refresh, - ); - } - - return const SizedBox(); - } - - return Scaffold( - appBar: AppBar( - leading: IconButton( - splashRadius: 20, - onPressed: () { - if (isNewSearch.value) { - isNewSearch.value = false; - } else { - context.popRoute(true); - } - }, - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - title: GestureDetector( - onTap: () { - isNewSearch.value = true; - searchFocusNode?.requestFocus(); - }, - child: isNewSearch.value ? buildTextField() : buildChip(), - ), - centerTitle: false, - ), - body: GestureDetector( - onTap: () { - if (searchFocusNode != null) { - searchFocusNode?.unfocus(); - } - - ref.watch(searchPageStateProvider.notifier).disableSearch(); - }, - child: Stack( - children: [ - buildSearchResult(), - if (isNewSearch.value) - SearchSuggestionList(onSubmitted: onSearchSubmitted), - ], - ), - ), - ); - } -} diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index f5c1a95d9..46cd7522d 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -31,7 +31,9 @@ import 'package:immich_mobile/modules/login/views/change_password_page.dart'; import 'package:immich_mobile/modules/login/views/login_page.dart'; import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/modules/onboarding/views/permission_onboarding_page.dart'; +import 'package:immich_mobile/modules/search/models/search_filter.dart'; import 'package:immich_mobile/modules/settings/views/settings_sub_page.dart'; +import 'package:immich_mobile/modules/search/views/search_input_page.dart'; import 'package:immich_mobile/modules/shared_link/models/shared_link.dart'; import 'package:immich_mobile/modules/shared_link/views/shared_link_edit_page.dart'; import 'package:immich_mobile/modules/shared_link/views/shared_link_page.dart'; @@ -43,7 +45,6 @@ import 'package:immich_mobile/modules/search/views/curated_location_page.dart'; import 'package:immich_mobile/modules/search/views/person_result_page.dart'; import 'package:immich_mobile/modules/search/views/recently_added_page.dart'; import 'package:immich_mobile/modules/search/views/search_page.dart'; -import 'package:immich_mobile/modules/search/views/search_result_page.dart'; import 'package:immich_mobile/modules/settings/views/settings_page.dart'; import 'package:immich_mobile/routing/auth_guard.dart'; import 'package:immich_mobile/routing/custom_transition_builders.dart'; @@ -125,10 +126,6 @@ class AppRouter extends _$AppRouter { page: BackupControllerRoute.page, guards: [_authGuard, _duplicateGuard, _backupPermissionGuard], ), - AutoRoute( - page: SearchResultRoute.page, - guards: [_authGuard, _duplicateGuard], - ), AutoRoute( page: CuratedLocationRoute.page, guards: [_authGuard, _duplicateGuard], @@ -223,6 +220,11 @@ class AppRouter extends _$AppRouter { page: BackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard], ), + CustomRoute( + page: SearchInputRoute.page, + guards: [_authGuard, _duplicateGuard], + transitionsBuilder: TransitionsBuilders.noTransition, + ), ]; } diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index cc86b701a..fa9fa32f6 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -255,22 +255,21 @@ abstract class _$AppRouter extends RootStackRouter { child: const RecentlyAddedPage(), ); }, - SearchRoute.name: (routeData) { - final args = routeData.argsAs( - orElse: () => const SearchRouteArgs()); + SearchInputRoute.name: (routeData) { + final args = routeData.argsAs( + orElse: () => const SearchInputRouteArgs()); return AutoRoutePage( routeData: routeData, - child: SearchPage(key: args.key), + child: SearchInputPage( + key: args.key, + prefilter: args.prefilter, + ), ); }, - SearchResultRoute.name: (routeData) { - final args = routeData.argsAs(); + SearchRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, - child: SearchResultPage( - key: args.key, - searchTerm: args.searchTerm, - ), + child: const SearchPage(), ); }, SelectAdditionalUserForSharingRoute.name: (routeData) { @@ -1113,69 +1112,55 @@ class RecentlyAddedRoute extends PageRouteInfo { } /// generated route for -/// [SearchPage] -class SearchRoute extends PageRouteInfo { - SearchRoute({ +/// [SearchInputPage] +class SearchInputRoute extends PageRouteInfo { + SearchInputRoute({ Key? key, + SearchFilter? prefilter, List? children, }) : super( + SearchInputRoute.name, + args: SearchInputRouteArgs( + key: key, + prefilter: prefilter, + ), + initialChildren: children, + ); + + static const String name = 'SearchInputRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class SearchInputRouteArgs { + const SearchInputRouteArgs({ + this.key, + this.prefilter, + }); + + final Key? key; + + final SearchFilter? prefilter; + + @override + String toString() { + return 'SearchInputRouteArgs{key: $key, prefilter: $prefilter}'; + } +} + +/// generated route for +/// [SearchPage] +class SearchRoute extends PageRouteInfo { + const SearchRoute({List? children}) + : super( SearchRoute.name, - args: SearchRouteArgs(key: key), initialChildren: children, ); static const String name = 'SearchRoute'; - static const PageInfo page = PageInfo(name); -} - -class SearchRouteArgs { - const SearchRouteArgs({this.key}); - - final Key? key; - - @override - String toString() { - return 'SearchRouteArgs{key: $key}'; - } -} - -/// generated route for -/// [SearchResultPage] -class SearchResultRoute extends PageRouteInfo { - SearchResultRoute({ - Key? key, - required String searchTerm, - List? children, - }) : super( - SearchResultRoute.name, - args: SearchResultRouteArgs( - key: key, - searchTerm: searchTerm, - ), - initialChildren: children, - ); - - static const String name = 'SearchResultRoute'; - - static const PageInfo page = - PageInfo(name); -} - -class SearchResultRouteArgs { - const SearchResultRouteArgs({ - this.key, - required this.searchTerm, - }); - - final Key? key; - - final String searchTerm; - - @override - String toString() { - return 'SearchResultRouteArgs{key: $key, searchTerm: $searchTerm}'; - } + static const PageInfo page = PageInfo(name); } /// generated route for diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart index dafbedd31..afe87fb24 100644 --- a/mobile/lib/routing/tab_navigation_observer.dart +++ b/mobile/lib/routing/tab_navigation_observer.dart @@ -38,7 +38,7 @@ class TabNavigationObserver extends AutoRouterObserver { if (route.name == 'SearchRoute') { // Refresh Location State ref.invalidate(getCuratedLocationProvider); - ref.invalidate(getCuratedPeopleProvider); + ref.invalidate(getAllPeopleProvider); } if (route.name == 'SharingRoute') { diff --git a/mobile/lib/shared/ui/asset_grid/multiselect_grid.dart b/mobile/lib/shared/ui/asset_grid/multiselect_grid.dart index 495f8f2f9..c9cc6c04a 100644 --- a/mobile/lib/shared/ui/asset_grid/multiselect_grid.dart +++ b/mobile/lib/shared/ui/asset_grid/multiselect_grid.dart @@ -43,6 +43,7 @@ class MultiselectGrid extends HookConsumerWidget { this.editEnabled = false, this.unarchive = false, this.unfavorite = false, + this.emptyIndicator, }); final ProviderListenable> renderListProvider; @@ -57,12 +58,12 @@ class MultiselectGrid extends HookConsumerWidget { final bool favoriteEnabled; final bool unfavorite; final bool editEnabled; - + final Widget? emptyIndicator; Widget buildDefaultLoadingIndicator() => const Center(child: ImmichLoadingIndicator()); Widget buildEmptyIndicator() => - const Center(child: Text("No assets to show")); + emptyIndicator ?? const Center(child: Text("No assets to show")); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/shared/views/tab_controller_page.dart b/mobile/lib/shared/views/tab_controller_page.dart index 40850bdb4..40de493d0 100644 --- a/mobile/lib/shared/views/tab_controller_page.dart +++ b/mobile/lib/shared/views/tab_controller_page.dart @@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart'; import 'package:immich_mobile/modules/home/providers/multiselect.provider.dart'; -import 'package:immich_mobile/modules/search/ui/immich_search_bar.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/providers/asset.provider.dart'; import 'package:immich_mobile/shared/providers/tab.provider.dart'; @@ -53,10 +52,6 @@ class TabControllerPage extends HookConsumerWidget { // Scroll to top scrollToTopNotifierProvider.scrollToTop(); } - if (tabsRouter.activeIndex == 1 && index == 1) { - // Focus search - searchFocusNotifier.requestFocus(); - } HapticFeedback.selectionClick(); tabsRouter.setActiveIndex(index); @@ -111,10 +106,7 @@ class TabControllerPage extends HookConsumerWidget { // Scroll to top scrollToTopNotifierProvider.scrollToTop(); } - if (tabsRouter.activeIndex == 1 && index == 1) { - // Focus search - searchFocusNotifier.requestFocus(); - } + HapticFeedback.selectionClick(); tabsRouter.setActiveIndex(index); ref.read(tabProvider.notifier).state = TabEnum.values[index]; @@ -170,11 +162,11 @@ class TabControllerPage extends HookConsumerWidget { final multiselectEnabled = ref.watch(multiselectProvider); return AutoTabsRouter( - routes: [ - const HomeRoute(), + routes: const [ + HomeRoute(), SearchRoute(), - const SharingRoute(), - const LibraryRoute(), + SharingRoute(), + LibraryRoute(), ], duration: const Duration(milliseconds: 600), transitionBuilder: (context, child, animation) => FadeTransition( diff --git a/mobile/lib/utils/immich_app_theme.dart b/mobile/lib/utils/immich_app_theme.dart index e2ed6cd56..07fac00e4 100644 --- a/mobile/lib/utils/immich_app_theme.dart +++ b/mobile/lib/utils/immich_app_theme.dart @@ -33,6 +33,9 @@ final ThemeData base = ThemeData( final ThemeData immichLightTheme = ThemeData( useMaterial3: true, brightness: Brightness.light, + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.indigo, + ), primarySwatch: Colors.indigo, primaryColor: Colors.indigo, hintColor: Colors.indigo, @@ -158,6 +161,10 @@ final ThemeData immichDarkTheme = ThemeData( brightness: Brightness.dark, primarySwatch: Colors.indigo, primaryColor: immichDarkThemePrimaryColor, + colorScheme: ColorScheme.fromSeed( + seedColor: immichDarkThemePrimaryColor, + brightness: Brightness.dark, + ), scaffoldBackgroundColor: immichDarkBackgroundColor, hintColor: Colors.grey[600], fontFamily: 'Overpass', diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index bf01dfcc8..6bf2b0902 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1,1816 +1,1808 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" - url: "https://pub.dev" - source: hosted - version: "67.0.0" - analyzer: - dependency: "direct overridden" - description: - name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" - url: "https://pub.dev" - source: hosted - version: "6.4.1" - analyzer_plugin: - dependency: "direct overridden" - description: - name: analyzer_plugin - sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" - url: "https://pub.dev" - source: hosted - version: "0.11.3" - ansicolor: - dependency: transitive - description: - name: ansicolor - sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - archive: - dependency: transitive - description: - name: archive - sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" - url: "https://pub.dev" - source: hosted - version: "3.4.10" - args: - dependency: transitive - description: - name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - async: - dependency: "direct main" - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - auto_route: - dependency: "direct main" - description: - name: auto_route - sha256: "82f8df1d177416bc6b7a449127d0270ff1f0f633a91f2ceb7a85d4f07c3affa1" - url: "https://pub.dev" - source: hosted - version: "7.8.4" - auto_route_generator: - dependency: "direct dev" - description: - name: auto_route_generator - sha256: "11067a3bcd643812518fe26c0c9ec073990286cabfd9d74b6da9ef9b913c4d22" - url: "https://pub.dev" - source: hosted - version: "7.3.2" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" - url: "https://pub.dev" - source: hosted - version: "4.0.1" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" - url: "https://pub.dev" - source: hosted - version: "2.4.8" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" - url: "https://pub.dev" - source: hosted - version: "7.3.0" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6 - url: "https://pub.dev" - source: hosted - version: "8.9.0" - cached_network_image: - dependency: "direct main" - description: - name: cached_network_image - sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" - url: "https://pub.dev" - source: hosted - version: "3.3.1" - cached_network_image_platform_interface: - dependency: transitive - description: - name: cached_network_image_platform_interface - sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - cached_network_image_web: - dependency: transitive - description: - name: cached_network_image_web - sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - cancellation_token: - dependency: transitive - description: - name: cancellation_token - sha256: ad95acf9d4b2f3563e25dc937f63587e46a70ce534e910b65d10e115490f1027 - url: "https://pub.dev" - source: hosted - version: "2.0.1" - cancellation_token_http: - dependency: "direct main" - description: - name: cancellation_token_http - sha256: "37ad2a20dba02aeb1f0a4d845e7a57eebacdb709e1186e0491e7cd81c559c4ff" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - chewie: - dependency: "direct main" - description: - name: chewie - sha256: "8bc4ac4cf3f316e50a25958c0f5eb9bb12cf7e8308bb1d74a43b230da2cfc144" - url: "https://pub.dev" - source: hosted - version: "1.7.5" - ci: - dependency: transitive - description: - name: ci - sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://pub.dev" - source: hosted - version: "0.4.1" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 - url: "https://pub.dev" - source: hosted - version: "4.10.0" - collection: - dependency: "direct main" - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - connectivity_plus: - dependency: "direct main" - description: - name: connectivity_plus - sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" - url: "https://pub.dev" - source: hosted - version: "5.0.2" - connectivity_plus_platform_interface: - dependency: transitive - description: - name: connectivity_plus_platform_interface - sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a - url: "https://pub.dev" - source: hosted - version: "1.2.4" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - cross_file: - dependency: transitive - description: - name: cross_file - sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e - url: "https://pub.dev" - source: hosted - version: "0.3.3+8" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - cupertino_icons: - dependency: transitive - description: - name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d - url: "https://pub.dev" - source: hosted - version: "1.0.6" - custom_lint: - dependency: "direct dev" - description: - name: custom_lint - sha256: f89ff83efdba7c8996e86bb3bad0b759d58f9b19ae4d0e277a386ddd8b481217 - url: "https://pub.dev" - source: hosted - version: "0.6.0" - custom_lint_builder: - dependency: transitive - description: - name: custom_lint_builder - sha256: "3a14687fc71a5e2124a29722106f7b7e67dd5a6d58e33f2859650b46acff1d54" - url: "https://pub.dev" - source: hosted - version: "0.6.1" - custom_lint_core: - dependency: transitive - description: - name: custom_lint_core - sha256: "1e9128e095ad5e0973469bdaac1ead8bfc86c485954c23cf617299de5e6fa029" - url: "https://pub.dev" - source: hosted - version: "0.6.1" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" - url: "https://pub.dev" - source: hosted - version: "2.3.4" - dartx: - dependency: transitive - description: - name: dartx - sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - dbus: - dependency: transitive - description: - name: dbus - sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" - url: "https://pub.dev" - source: hosted - version: "0.7.10" - device_info_plus: - dependency: "direct main" - description: - name: device_info_plus - sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" - url: "https://pub.dev" - source: hosted - version: "9.1.2" - device_info_plus_platform_interface: - dependency: transitive - description: - name: device_info_plus_platform_interface - sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 - url: "https://pub.dev" - source: hosted - version: "7.0.0" - easy_image_viewer: - dependency: "direct main" - description: - name: easy_image_viewer - sha256: "750bb85e0a34504557d378a616110540caeec2324490fc040709589219e75834" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - easy_localization: - dependency: "direct main" - description: - name: easy_localization - sha256: "9c86754b22aaa3e74e471635b25b33729f958dd6fb83df0ad6612948a7b231af" - url: "https://pub.dev" - source: hosted - version: "3.0.4" - easy_logger: - dependency: transitive - description: - name: easy_logger - sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 - url: "https://pub.dev" - source: hosted - version: "0.0.2" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - file_selector_linux: - dependency: transitive - description: - name: file_selector_linux - sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" - url: "https://pub.dev" - source: hosted - version: "0.9.2+1" - file_selector_macos: - dependency: transitive - description: - name: file_selector_macos - sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 - url: "https://pub.dev" - source: hosted - version: "0.9.3+3" - file_selector_platform_interface: - dependency: transitive - description: - name: file_selector_platform_interface - sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b - url: "https://pub.dev" - source: hosted - version: "2.6.2" - file_selector_windows: - dependency: transitive - description: - name: file_selector_windows - sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 - url: "https://pub.dev" - source: hosted - version: "0.9.3+1" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_cache_manager: - dependency: "direct main" - description: - name: flutter_cache_manager - sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" - url: "https://pub.dev" - source: hosted - version: "3.3.1" - flutter_displaymode: - dependency: "direct main" - description: - name: flutter_displaymode - sha256: "42c5e9abd13d28ed74f701b60529d7f8416947e58256e6659c5550db719c57ef" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - flutter_driver: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - flutter_hooks: - dependency: "direct main" - description: - name: flutter_hooks - sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70 - url: "https://pub.dev" - source: hosted - version: "0.20.5" - flutter_launcher_icons: - dependency: "direct dev" - description: - name: flutter_launcher_icons - sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" - url: "https://pub.dev" - source: hosted - version: "0.13.1" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 - url: "https://pub.dev" - source: hosted - version: "3.0.1" - flutter_local_notifications: - dependency: "direct main" - description: - name: flutter_local_notifications - sha256: c18f1de98fe0bb9dd5ba91e1330d4febc8b6a7de6aae3ffe475ef423723e72f3 - url: "https://pub.dev" - source: hosted - version: "16.3.2" - flutter_local_notifications_linux: - dependency: transitive - description: - name: flutter_local_notifications_linux - sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" - url: "https://pub.dev" - source: hosted - version: "4.0.0+1" - flutter_local_notifications_platform_interface: - dependency: transitive - description: - name: flutter_local_notifications_platform_interface - sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" - url: "https://pub.dev" - source: hosted - version: "7.0.0+1" - flutter_localizations: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - flutter_native_splash: - dependency: "direct dev" - description: - name: flutter_native_splash - sha256: "558f10070f03ee71f850a78f7136ab239a67636a294a44a06b6b7345178edb1e" - url: "https://pub.dev" - source: hosted - version: "2.3.10" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da - url: "https://pub.dev" - source: hosted - version: "2.0.17" - flutter_riverpod: - dependency: transitive - description: - name: flutter_riverpod - sha256: "4bce556b7ecbfea26109638d5237684538d4abc509d253e6c5c4c5733b360098" - url: "https://pub.dev" - source: hosted - version: "2.4.10" - flutter_svg: - dependency: "direct main" - description: - name: flutter_svg - sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c - url: "https://pub.dev" - source: hosted - version: "2.0.9" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_udid: - dependency: "direct main" - description: - name: flutter_udid - sha256: "63384bd96203aaefccfd7137fab642edda18afede12b0e9e1a2c96fe2589fd07" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - flutter_web_auth: - dependency: "direct main" - description: - name: flutter_web_auth - sha256: a69fa8f43b9e4d86ac72176bf747b735e7b977dd7cf215076d95b87cb05affdd - url: "https://pub.dev" - source: hosted - version: "0.5.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - fluttertoast: - dependency: "direct main" - description: - name: fluttertoast - sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1 - url: "https://pub.dev" - source: hosted - version: "8.2.4" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d - url: "https://pub.dev" - source: hosted - version: "2.4.1" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - fuchsia_remote_debug_protocol: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - geolocator: - dependency: "direct main" - description: - name: geolocator - sha256: "694ec58afe97787b5b72b8a0ab78c1a9244811c3c10e72c4362ef3c0ceb005cd" - url: "https://pub.dev" - source: hosted - version: "11.0.0" - geolocator_android: - dependency: transitive - description: - name: geolocator_android - sha256: "136f1c97e1903366393bda514c5d9e98843418baea52899aa45edae9af8a5cd6" - url: "https://pub.dev" - source: hosted - version: "4.5.2" - geolocator_apple: - dependency: transitive - description: - name: geolocator_apple - sha256: "2f2d4ee16c4df269e93c0e382be075cc01d5db6703c3196e4af20a634fe49ef4" - url: "https://pub.dev" - source: hosted - version: "2.3.6" - geolocator_platform_interface: - dependency: transitive - description: - name: geolocator_platform_interface - sha256: "009a21c4bc2761e58dccf07c24f219adaebe0ff707abdfd40b0a763d4003fab9" - url: "https://pub.dev" - source: hosted - version: "4.2.2" - geolocator_web: - dependency: transitive - description: - name: geolocator_web - sha256: "49d8f846ebeb5e2b6641fe477a7e97e5dd73f03cbfef3fd5c42177b7300fb0ed" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - geolocator_windows: - dependency: transitive - description: - name: geolocator_windows - sha256: a92fae29779d5c6dc60e8411302f5221ade464968fe80a36d330e80a71f087af - url: "https://pub.dev" - source: hosted - version: "0.2.2" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - graphs: - dependency: transitive - description: - name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 - url: "https://pub.dev" - source: hosted - version: "2.3.1" - hooks_riverpod: - dependency: "direct main" - description: - name: hooks_riverpod - sha256: "758b07eba336e3cbacbd81dba481f2228a14102083fdde07045e8514e8054c49" - url: "https://pub.dev" - source: hosted - version: "2.4.10" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e - url: "https://pub.dev" - source: hosted - version: "4.2.0" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" - http: - dependency: "direct main" - description: - name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" - url: "https://pub.dev" - source: hosted - version: "0.13.6" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - http_parser: - dependency: "direct main" - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - image: - dependency: transitive - description: - name: image - sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" - url: "https://pub.dev" - source: hosted - version: "4.1.7" - image_picker: - dependency: "direct main" - description: - name: image_picker - sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd" - url: "https://pub.dev" - source: hosted - version: "1.0.7" - image_picker_android: - dependency: transitive - description: - name: image_picker_android - sha256: "39f2bfe497e495450c81abcd44b62f56c2a36a37a175da7d137b4454977b51b1" - url: "https://pub.dev" - source: hosted - version: "0.8.9+3" - image_picker_for_web: - dependency: transitive - description: - name: image_picker_for_web - sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 - url: "https://pub.dev" - source: hosted - version: "3.0.2" - image_picker_ios: - dependency: transitive - description: - name: image_picker_ios - sha256: fadafce49e8569257a0cad56d24438a6fa1f0cbd7ee0af9b631f7492818a4ca3 - url: "https://pub.dev" - source: hosted - version: "0.8.9+1" - image_picker_linux: - dependency: transitive - description: - name: image_picker_linux - sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" - url: "https://pub.dev" - source: hosted - version: "0.2.1+1" - image_picker_macos: - dependency: transitive - description: - name: image_picker_macos - sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" - url: "https://pub.dev" - source: hosted - version: "0.2.1+1" - image_picker_platform_interface: - dependency: transitive - description: - name: image_picker_platform_interface - sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b - url: "https://pub.dev" - source: hosted - version: "2.9.3" - image_picker_windows: - dependency: transitive - description: - name: image_picker_windows - sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" - url: "https://pub.dev" - source: hosted - version: "0.2.1+1" - integration_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - intl: - dependency: "direct main" - description: - name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" - url: "https://pub.dev" - source: hosted - version: "0.18.1" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - isar: - dependency: "direct main" - description: - name: isar - sha256: "99165dadb2cf2329d3140198363a7e7bff9bbd441871898a87e26914d25cf1ea" - url: "https://pub.dev" - source: hosted - version: "3.1.0+1" - isar_flutter_libs: - dependency: "direct main" - description: - name: isar_flutter_libs - sha256: bc6768cc4b9c61aabff77152e7f33b4b17d2fc93134f7af1c3dd51500fe8d5e8 - url: "https://pub.dev" - source: hosted - version: "3.1.0+1" - isar_generator: - dependency: "direct dev" - description: - name: isar_generator - sha256: "76c121e1295a30423604f2f819bc255bc79f852f3bc8743a24017df6068ad133" - url: "https://pub.dev" - source: hosted - version: "3.1.0+1" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.dev" - source: hosted - version: "4.8.1" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" - url: "https://pub.dev" - source: hosted - version: "10.0.0" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 - url: "https://pub.dev" - source: hosted - version: "2.0.1" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 - url: "https://pub.dev" - source: hosted - version: "2.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 - url: "https://pub.dev" - source: hosted - version: "3.0.0" - logging: - dependency: "direct main" - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - maplibre_gl: - dependency: "direct main" - description: - path: "." - ref: acb428a005efd9832a0a8e7ef0945f899dfb3ca5 - resolved-ref: acb428a005efd9832a0a8e7ef0945f899dfb3ca5 - url: "https://github.com/maplibre/flutter-maplibre-gl.git" - source: git - version: "0.18.0" - maplibre_gl_platform_interface: - dependency: transitive - description: - path: maplibre_gl_platform_interface - ref: main - resolved-ref: "3cf0abb051849ca3f14e6aa19d2261ad18f22ce1" - url: "https://github.com/maplibre/flutter-maplibre-gl.git" - source: git - version: "0.18.0" - maplibre_gl_web: - dependency: transitive - description: - path: maplibre_gl_web - ref: main - resolved-ref: "3cf0abb051849ca3f14e6aa19d2261ad18f22ce1" - url: "https://github.com/maplibre/flutter-maplibre-gl.git" - source: git - version: "0.18.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" - url: "https://pub.dev" - source: hosted - version: "0.8.0" - meta: - dependency: "direct overridden" - description: - name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" - url: "https://pub.dev" - source: hosted - version: "1.12.0" - mime: - dependency: transitive - description: - name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" - url: "https://pub.dev" - source: hosted - version: "1.0.5" - mocktail: - dependency: "direct dev" - description: - name: mocktail - sha256: c4b5007d91ca4f67256e720cb1b6d704e79a510183a12fa551021f652577dce6 - url: "https://pub.dev" - source: hosted - version: "1.0.3" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - nm: - dependency: transitive - description: - name: nm - sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" - url: "https://pub.dev" - source: hosted - version: "0.5.0" - octo_image: - dependency: "direct main" - description: - name: octo_image - sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - openapi: - dependency: "direct main" - description: - path: openapi - relative: true - source: path - version: "1.0.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - package_info_plus: - dependency: "direct main" - description: - name: package_info_plus - sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" - url: "https://pub.dev" - source: hosted - version: "5.0.1" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - path: - dependency: "direct main" - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - path_parsing: - dependency: transitive - description: - name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf - url: "https://pub.dev" - source: hosted - version: "1.0.1" - path_provider: - dependency: "direct main" - description: - name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - path_provider_ios: - dependency: "direct main" - description: - name: path_provider_ios - sha256: "03d639406f5343478352433f00d3c4394d52dac8df3d847869c5e2333e0bbce8" - url: "https://pub.dev" - source: hosted - version: "2.0.11" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - permission_handler: - dependency: "direct main" - description: - name: permission_handler - sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44" - url: "https://pub.dev" - source: hosted - version: "11.3.0" - permission_handler_android: - dependency: transitive - description: - name: permission_handler_android - sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" - url: "https://pub.dev" - source: hosted - version: "12.0.5" - permission_handler_apple: - dependency: transitive - description: - name: permission_handler_apple - sha256: bdafc6db74253abb63907f4e357302e6bb786ab41465e8635f362ee71fd8707b - url: "https://pub.dev" - source: hosted - version: "9.4.0" - permission_handler_html: - dependency: transitive - description: - name: permission_handler_html - sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" - url: "https://pub.dev" - source: hosted - version: "0.1.1" - permission_handler_platform_interface: - dependency: transitive - description: - name: permission_handler_platform_interface - sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - permission_handler_windows: - dependency: transitive - description: - name: permission_handler_windows - sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" - url: "https://pub.dev" - source: hosted - version: "0.2.1" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - photo_manager: - dependency: "direct main" - description: - name: photo_manager - sha256: "8cf79918f6de9843b394a1670fe1aec54ebcac852b4b4c9ef88211894547dc61" - url: "https://pub.dev" - source: hosted - version: "3.0.0-dev.5" - photo_manager_image_provider: - dependency: "direct main" - description: - name: photo_manager_image_provider - sha256: c187f60c3fdbe5630735d9a0bccbb071397ec03dcb1ba6085c29c8adece798a0 - url: "https://pub.dev" - source: hosted - version: "2.1.0" - platform: - dependency: transitive - description: - name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" - url: "https://pub.dev" - source: hosted - version: "3.1.4" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - pointycastle: - dependency: transitive - description: - name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" - url: "https://pub.dev" - source: hosted - version: "3.7.4" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" - url: "https://pub.dev" - source: hosted - version: "5.0.2" - provider: - dependency: transitive - description: - name: provider - sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" - url: "https://pub.dev" - source: hosted - version: "6.1.1" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 - url: "https://pub.dev" - source: hosted - version: "1.2.3" - riverpod: - dependency: transitive - description: - name: riverpod - sha256: "548e2192eb7aeb826eb89387f814edb76594f3363e2c0bb99dd733d795ba3589" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - riverpod_analyzer_utils: - dependency: transitive - description: - name: riverpod_analyzer_utils - sha256: "8b71f03fc47ae27d13769496a1746332df4cec43918aeba9aff1e232783a780f" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - riverpod_annotation: - dependency: "direct main" - description: - name: riverpod_annotation - sha256: "77e5d51afa4fa3e67903fb8746f33d368728d7051a0b6c292bcee60aeba46d95" - url: "https://pub.dev" - source: hosted - version: "2.3.4" - riverpod_generator: - dependency: "direct dev" - description: - name: riverpod_generator - sha256: "359068f04879347ae4edbe66c81cc95f83fa1743806d1a0c86e55dd3c33ebb32" - url: "https://pub.dev" - source: hosted - version: "2.3.11" - riverpod_lint: - dependency: "direct dev" - description: - name: riverpod_lint - sha256: e9bbd02e9e89e18eecb183bbca556d7b523a0669024da9b8167c08903f442937 - url: "https://pub.dev" - source: hosted - version: "2.3.9" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.dev" - source: hosted - version: "0.27.7" - scrollable_positioned_list: - dependency: "direct main" - description: - name: scrollable_positioned_list - sha256: "1b54d5f1329a1e263269abc9e2543d90806131aa14fe7c6062a8054d57249287" - url: "https://pub.dev" - source: hosted - version: "0.3.8" - share_plus: - dependency: "direct main" - description: - name: share_plus - sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900" - url: "https://pub.dev" - source: hosted - version: "7.2.2" - share_plus_platform_interface: - dependency: transitive - description: - name: share_plus_platform_interface - sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956 - url: "https://pub.dev" - source: hosted - version: "3.3.1" - shared_preferences: - dependency: transitive - description: - name: shared_preferences - sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" - url: "https://pub.dev" - source: hosted - version: "2.3.5" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - socket_io_client: - dependency: "direct main" - description: - name: socket_io_client - sha256: ede469f3e4c55e8528b4e023bdedbc20832e8811ab9b61679d1ba3ed5f01f23b - url: "https://pub.dev" - source: hosted - version: "2.0.3+1" - socket_io_common: - dependency: transitive - description: - name: socket_io_common - sha256: "2ab92f8ff3ebbd4b353bf4a98bee45cc157e3255464b2f90f66e09c4472047eb" - url: "https://pub.dev" - source: hosted - version: "2.0.3" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 - url: "https://pub.dev" - source: hosted - version: "2.3.2" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" - url: "https://pub.dev" - source: hosted - version: "2.5.3" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - state_notifier: - dependency: transitive - description: - name: state_notifier - sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb - url: "https://pub.dev" - source: hosted - version: "1.0.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - sync_http: - dependency: transitive - description: - name: sync_http - sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" - url: "https://pub.dev" - source: hosted - version: "0.3.1" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" - url: "https://pub.dev" - source: hosted - version: "3.1.0+1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" - url: "https://pub.dev" - source: hosted - version: "0.6.1" - thumbhash: - dependency: "direct main" - description: - name: thumbhash - sha256: "5f6d31c5279ca0b5caa81ec10aae8dcaab098d82cb699ea66ada4ed09c794a37" - url: "https://pub.dev" - source: hosted - version: "0.1.0+1" - time: - dependency: transitive - description: - name: time - sha256: ad8e018a6c9db36cb917a031853a1aae49467a93e0d464683e029537d848c221 - url: "https://pub.dev" - source: hosted - version: "2.1.4" - timezone: - dependency: "direct main" - description: - name: timezone - sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" - url: "https://pub.dev" - source: hosted - version: "0.9.2" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - universal_io: - dependency: transitive - description: - name: universal_io - sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c - url: "https://pub.dev" - source: hosted - version: "6.2.4" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 - url: "https://pub.dev" - source: hosted - version: "6.3.0" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" - url: "https://pub.dev" - source: hosted - version: "6.2.4" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 - url: "https://pub.dev" - source: hosted - version: "3.1.1" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 - url: "https://pub.dev" - source: hosted - version: "3.1.0" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b - url: "https://pub.dev" - source: hosted - version: "2.2.3" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 - url: "https://pub.dev" - source: hosted - version: "3.1.1" - uuid: - dependency: transitive - description: - name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 - url: "https://pub.dev" - source: hosted - version: "4.3.3" - vector_graphics: - dependency: transitive - description: - name: vector_graphics - sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752" - url: "https://pub.dev" - source: hosted - version: "1.1.10+1" - vector_graphics_codec: - dependency: transitive - description: - name: vector_graphics_codec - sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33 - url: "https://pub.dev" - source: hosted - version: "1.1.10+1" - vector_graphics_compiler: - dependency: transitive - description: - name: vector_graphics_compiler - sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a" - url: "https://pub.dev" - source: hosted - version: "1.1.10+1" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - video_player: - dependency: "direct main" - description: - name: video_player - sha256: fbf28ce8bcfe709ad91b5789166c832cb7a684d14f571a81891858fefb5bb1c2 - url: "https://pub.dev" - source: hosted - version: "2.8.2" - video_player_android: - dependency: transitive - description: - name: video_player_android - sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2" - url: "https://pub.dev" - source: hosted - version: "2.4.12" - video_player_avfoundation: - dependency: transitive - description: - name: video_player_avfoundation - sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" - url: "https://pub.dev" - source: hosted - version: "2.5.6" - video_player_platform_interface: - dependency: transitive - description: - name: video_player_platform_interface - sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" - url: "https://pub.dev" - source: hosted - version: "6.2.2" - video_player_web: - dependency: transitive - description: - name: video_player_web - sha256: "34beb3a07d4331a24f7e7b2f75b8e2b103289038e07e65529699a671b6a6e2cb" - url: "https://pub.dev" - source: hosted - version: "2.1.3" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 - url: "https://pub.dev" - source: hosted - version: "13.0.0" - wakelock_plus: - dependency: "direct main" - description: - name: wakelock_plus - sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d - url: "https://pub.dev" - source: hosted - version: "1.1.4" - wakelock_plus_platform_interface: - dependency: transitive - description: - name: wakelock_plus_platform_interface - sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 - url: "https://pub.dev" - source: hosted - version: "0.3.0" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" - webdriver: - dependency: transitive - description: - name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - win32: - dependency: transitive - description: - name: win32 - sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" - url: "https://pub.dev" - source: hosted - version: "5.2.0" - win32_registry: - dependency: transitive - description: - name: win32_registry - sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" - url: "https://pub.dev" - source: hosted - version: "1.1.2" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d - url: "https://pub.dev" - source: hosted - version: "1.0.4" - xml: - dependency: transitive - description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" - xxh3: - dependency: transitive - description: - name: xxh3 - sha256: a92b30944a9aeb4e3d4f3c3d4ddb3c7816ca73475cd603682c4f8149690f56d7 - url: "https://pub.dev" - source: hosted - version: "1.0.1" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" -sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "0f7b1783ddb1e4600580b8c00d0ddae5b06ae7f0382bd4fcce5db4df97b618e1" + url: "https://pub.dev" + source: hosted + version: "66.0.0" + analyzer: + dependency: "direct overridden" + description: + name: analyzer + sha256: "5e8bdcda061d91da6b034d64d8e4026f355bcb8c3e7a0ac2da1523205a91a737" + url: "https://pub.dev" + source: hosted + version: "6.4.0" + analyzer_plugin: + dependency: "direct overridden" + description: + name: analyzer_plugin + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + url: "https://pub.dev" + source: hosted + version: "0.11.3" + ansicolor: + dependency: transitive + description: + name: ansicolor + sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + archive: + dependency: transitive + description: + name: archive + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" + url: "https://pub.dev" + source: hosted + version: "3.4.10" + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + async: + dependency: "direct main" + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + auto_route: + dependency: "direct main" + description: + name: auto_route + sha256: "82f8df1d177416bc6b7a449127d0270ff1f0f633a91f2ceb7a85d4f07c3affa1" + url: "https://pub.dev" + source: hosted + version: "7.8.4" + auto_route_generator: + dependency: "direct dev" + description: + name: auto_route_generator + sha256: "11067a3bcd643812518fe26c0c9ec073990286cabfd9d74b6da9ef9b913c4d22" + url: "https://pub.dev" + source: hosted + version: "7.3.2" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + url: "https://pub.dev" + source: hosted + version: "2.4.8" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" + url: "https://pub.dev" + source: hosted + version: "7.2.10" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" + url: "https://pub.dev" + source: hosted + version: "8.6.1" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" + url: "https://pub.dev" + source: hosted + version: "3.3.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + cancellation_token: + dependency: transitive + description: + name: cancellation_token + sha256: ad95acf9d4b2f3563e25dc937f63587e46a70ce534e910b65d10e115490f1027 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + cancellation_token_http: + dependency: "direct main" + description: + name: cancellation_token_http + sha256: "37ad2a20dba02aeb1f0a4d845e7a57eebacdb709e1186e0491e7cd81c559c4ff" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + chewie: + dependency: "direct main" + description: + name: chewie + sha256: "3427e469d7cc99536ac4fbaa069b3352c21760263e65ffb4f0e1c054af43a73e" + url: "https://pub.dev" + source: hosted + version: "1.7.4" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + url: "https://pub.dev" + source: hosted + version: "0.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" + url: "https://pub.dev" + source: hosted + version: "4.5.0" + collection: + dependency: "direct main" + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" + source: hosted + version: "1.2.4" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + url: "https://pub.dev" + source: hosted + version: "0.3.3+4" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" + source: hosted + version: "1.0.5" + custom_lint: + dependency: "direct dev" + description: + name: custom_lint + sha256: "7c0aec12df22f9082146c354692056677f1e70bc43471644d1fdb36c6fdda799" + url: "https://pub.dev" + source: hosted + version: "0.6.4" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: d7dc41e709dde223806660268678be7993559e523eb3164e2a1425fd6f7615a9 + url: "https://pub.dev" + source: hosted + version: "0.6.4" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: a85e8f78f4c52f6c63cdaf8c872eb573db0231dcdf3c3a5906d493c1f8bc20e6 + url: "https://pub.dev" + source: hosted + version: "0.6.3" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + dartx: + dependency: transitive + description: + name: dartx + sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" + url: "https://pub.dev" + source: hosted + version: "9.1.1" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + url: "https://pub.dev" + source: hosted + version: "7.0.0" + easy_image_viewer: + dependency: "direct main" + description: + name: easy_image_viewer + sha256: "6d765e9040a6e625796b387140b95f23318f25a448bf2647af30d17a77cea022" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + sha256: de63e3b422adfc97f256cbb3f8cf12739b6a4993d390f3cadb3f51837afaefe5 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + easy_logger: + dependency: transitive + description: + name: easy_logger + sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 + url: "https://pub.dev" + source: hosted + version: "0.0.2" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "770eb1ab057b5ae4326d1c24cc57710758b9a46026349d021d6311bd27580046" + url: "https://pub.dev" + source: hosted + version: "0.9.2" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: "4ada532862917bf16e3adb3891fe3a5917a58bae03293e497082203a80909412" + url: "https://pub.dev" + source: hosted + version: "0.9.3+1" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: "412705a646a0ae90f33f37acfae6a0f7cbc02222d6cd34e479421c3e74d3853c" + url: "https://pub.dev" + source: hosted + version: "2.6.0" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "1372760c6b389842b77156203308940558a2817360154084368608413835fc26" + url: "https://pub.dev" + source: hosted + version: "0.9.3" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_cache_manager: + dependency: "direct main" + description: + name: flutter_cache_manager + sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" + url: "https://pub.dev" + source: hosted + version: "3.3.1" + flutter_displaymode: + dependency: "direct main" + description: + name: flutter_displaymode + sha256: "42c5e9abd13d28ed74f701b60529d7f8416947e58256e6659c5550db719c57ef" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_hooks: + dependency: "direct main" + description: + name: flutter_hooks + sha256: "09f64db63fee3b2ab8b9038a1346be7d8986977fae3fec601275bf32455ccfc0" + url: "https://pub.dev" + source: hosted + version: "0.20.4" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" + source: hosted + version: "0.13.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 + url: "https://pub.dev" + source: hosted + version: "3.0.1" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: c18f1de98fe0bb9dd5ba91e1330d4febc8b6a7de6aae3ffe475ef423723e72f3 + url: "https://pub.dev" + source: hosted + version: "16.3.2" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" + url: "https://pub.dev" + source: hosted + version: "4.0.0+1" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" + url: "https://pub.dev" + source: hosted + version: "7.0.0+1" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_native_splash: + dependency: "direct dev" + description: + name: flutter_native_splash + sha256: "558f10070f03ee71f850a78f7136ab239a67636a294a44a06b6b7345178edb1e" + url: "https://pub.dev" + source: hosted + version: "2.3.10" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360" + url: "https://pub.dev" + source: hosted + version: "2.0.15" + flutter_riverpod: + dependency: transitive + description: + name: flutter_riverpod + sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d" + url: "https://pub.dev" + source: hosted + version: "2.5.1" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + url: "https://pub.dev" + source: hosted + version: "2.0.9" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_udid: + dependency: "direct main" + description: + name: flutter_udid + sha256: "63384bd96203aaefccfd7137fab642edda18afede12b0e9e1a2c96fe2589fd07" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + flutter_web_auth: + dependency: "direct main" + description: + name: flutter_web_auth + sha256: a69fa8f43b9e4d86ac72176bf747b735e7b977dd7cf215076d95b87cb05affdd + url: "https://pub.dev" + source: hosted + version: "0.5.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1 + url: "https://pub.dev" + source: hosted + version: "8.2.4" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + url: "https://pub.dev" + source: hosted + version: "2.4.1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + geolocator: + dependency: "direct main" + description: + name: geolocator + sha256: "694ec58afe97787b5b72b8a0ab78c1a9244811c3c10e72c4362ef3c0ceb005cd" + url: "https://pub.dev" + source: hosted + version: "11.0.0" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + sha256: "93906636752ea4d4e778afa981fdfe7409f545b3147046300df194330044d349" + url: "https://pub.dev" + source: hosted + version: "4.3.1" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + sha256: "79babf44b692ec5e789d322dc736ef71586056e8e6828f747c9e005456b248bf" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + sha256: b8cc1d3be0ca039a3f2174b0b026feab8af3610e220b8532e42cff8ec6658535 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + sha256: "49d8f846ebeb5e2b6641fe477a7e97e5dd73f03cbfef3fd5c42177b7300fb0ed" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + sha256: a92fae29779d5c6dc60e8411302f5221ade464968fe80a36d330e80a71f087af + url: "https://pub.dev" + source: hosted + version: "0.2.2" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + hooks_riverpod: + dependency: "direct main" + description: + name: hooks_riverpod + sha256: "45b2030a18bcd6dbd680c2c91bc3b33e3fe7c323e3acb5ecec93a613e2fbaa8a" + url: "https://pub.dev" + source: hosted + version: "2.5.1" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: "94ee21a60ea2836500799f3af035dc3212b1562027f1e0031c14e087f0231449" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + http: + dependency: "direct main" + description: + name: http + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + url: "https://pub.dev" + source: hosted + version: "0.13.6" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: "direct main" + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + image: + dependency: transitive + description: + name: image + sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd" + url: "https://pub.dev" + source: hosted + version: "1.0.7" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "8179b54039b50eee561676232304f487602e2950ffb3e8995ed9034d6505ca34" + url: "https://pub.dev" + source: hosted + version: "0.8.7+4" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "869fe8a64771b7afbc99fc433a5f7be2fea4d1cb3d7c11a48b6b579eb9c797f0" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: b3e2f21feb28b24dd73a35d7ad6e83f568337c70afab5eabac876e23803f264b + url: "https://pub.dev" + source: hosted + version: "0.8.8" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "02cbc21fe1706b97942b575966e5fbbeaac535e76deef70d3a242e4afb857831" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: cee2aa86c56780c13af2c77b5f2f72973464db204569e1ba2dd744459a065af4 + url: "https://pub.dev" + source: hosted + version: "0.2.1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: c1134543ae2187e85299996d21c526b2f403854994026d575ae4cf30d7bb2a32 + url: "https://pub.dev" + source: hosted + version: "2.9.0" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: c3066601ea42113922232c7b7b3330a2d86f029f685bba99d82c30e799914952 + url: "https://pub.dev" + source: hosted + version: "0.2.1" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + isar: + dependency: "direct main" + description: + name: isar + sha256: "99165dadb2cf2329d3140198363a7e7bff9bbd441871898a87e26914d25cf1ea" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" + isar_flutter_libs: + dependency: "direct main" + description: + name: isar_flutter_libs + sha256: bc6768cc4b9c61aabff77152e7f33b4b17d2fc93134f7af1c3dd51500fe8d5e8 + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" + isar_generator: + dependency: "direct dev" + description: + name: isar_generator + sha256: "76c121e1295a30423604f2f819bc255bc79f852f3bc8743a24017df6068ad133" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + logging: + dependency: "direct main" + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + maplibre_gl: + dependency: "direct main" + description: + path: "." + ref: acb428a005efd9832a0a8e7ef0945f899dfb3ca5 + resolved-ref: acb428a005efd9832a0a8e7ef0945f899dfb3ca5 + url: "https://github.com/maplibre/flutter-maplibre-gl.git" + source: git + version: "0.18.0" + maplibre_gl_platform_interface: + dependency: transitive + description: + path: maplibre_gl_platform_interface + ref: main + resolved-ref: acb428a005efd9832a0a8e7ef0945f899dfb3ca5 + url: "https://github.com/maplibre/flutter-maplibre-gl.git" + source: git + version: "0.18.0" + maplibre_gl_web: + dependency: transitive + description: + path: maplibre_gl_web + ref: main + resolved-ref: acb428a005efd9832a0a8e7ef0945f899dfb3ca5 + url: "https://github.com/maplibre/flutter-maplibre-gl.git" + source: git + version: "0.18.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: "direct overridden" + description: + name: meta + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + mocktail: + dependency: "direct dev" + description: + name: mocktail + sha256: c4b5007d91ca4f67256e720cb1b6d704e79a510183a12fa551021f652577dce6 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + octo_image: + dependency: "direct main" + description: + name: octo_image + sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + openapi: + dependency: "direct main" + description: + path: openapi + relative: true + source: path + version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + path: + dependency: "direct main" + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + path_provider_ios: + dependency: "direct main" + description: + name: path_provider_ios + sha256: "03d639406f5343478352433f00d3c4394d52dac8df3d847869c5e2333e0bbce8" + url: "https://pub.dev" + source: hosted + version: "2.0.11" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da + url: "https://pub.dev" + source: hosted + version: "2.2.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "45ff3fbcb99040fde55c528d5e3e6ca29171298a85436274d49c6201002087d6" + url: "https://pub.dev" + source: hosted + version: "11.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "758284a0976772f9c744d6384fc5dc4834aa61e3f7aa40492927f244767374eb" + url: "https://pub.dev" + source: hosted + version: "12.0.3" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: c6bf440f80acd2a873d3d91a699e4cc770f86e7e6b576dda98759e8b92b39830 + url: "https://pub.dev" + source: hosted + version: "9.3.0" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" + url: "https://pub.dev" + source: hosted + version: "0.1.1" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "5c43148f2bfb6d14c5a8162c0a712afe891f2d847f35fcff29c406b37da43c3c" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + photo_manager: + dependency: "direct main" + description: + name: photo_manager + sha256: "8cf79918f6de9843b394a1670fe1aec54ebcac852b4b4c9ef88211894547dc61" + url: "https://pub.dev" + source: hosted + version: "3.0.0-dev.5" + photo_manager_image_provider: + dependency: "direct main" + description: + name: photo_manager_image_provider + sha256: c187f60c3fdbe5630735d9a0bccbb071397ec03dcb1ba6085c29c8adece798a0 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" + source: hosted + version: "3.7.3" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d + url: "https://pub.dev" + source: hosted + version: "2.5.1" + riverpod_analyzer_utils: + dependency: transitive + description: + name: riverpod_analyzer_utils + sha256: "8b71f03fc47ae27d13769496a1746332df4cec43918aeba9aff1e232783a780f" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + riverpod_annotation: + dependency: "direct main" + description: + name: riverpod_annotation + sha256: b70e95fbd5ca7ce42f5148092022971bb2e9843b6ab71e97d479e8ab52e98979 + url: "https://pub.dev" + source: hosted + version: "2.3.3" + riverpod_generator: + dependency: "direct dev" + description: + name: riverpod_generator + sha256: ff8f064f1d7ef3cc6af481bba8e9a3fcdb4d34df34fac1b39bbc003167065be0 + url: "https://pub.dev" + source: hosted + version: "2.3.9" + riverpod_lint: + dependency: "direct dev" + description: + name: riverpod_lint + sha256: "3c67c14ccd16f0c9d53e35ef70d06cd9d072e2fb14557326886bbde903b230a5" + url: "https://pub.dev" + source: hosted + version: "2.3.10" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + scrollable_positioned_list: + dependency: "direct main" + description: + name: scrollable_positioned_list + sha256: "1b54d5f1329a1e263269abc9e2543d90806131aa14fe7c6062a8054d57249287" + url: "https://pub.dev" + source: hosted + version: "0.3.8" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900" + url: "https://pub.dev" + source: hosted + version: "7.2.2" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956 + url: "https://pub.dev" + source: hosted + version: "3.3.1" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: d29753996d8eb8f7619a1f13df6ce65e34bc107bef6330739ed76f18b22310ef + url: "https://pub.dev" + source: hosted + version: "2.3.3" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + socket_io_client: + dependency: "direct main" + description: + name: socket_io_client + sha256: ede469f3e4c55e8528b4e023bdedbc20832e8811ab9b61679d1ba3ed5f01f23b + url: "https://pub.dev" + source: hosted + version: "2.0.3+1" + socket_io_common: + dependency: transitive + description: + name: socket_io_common + sha256: "2ab92f8ff3ebbd4b353bf4a98bee45cc157e3255464b2f90f66e09c4472047eb" + url: "https://pub.dev" + source: hosted + version: "2.0.3" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + thumbhash: + dependency: "direct main" + description: + name: thumbhash + sha256: "5f6d31c5279ca0b5caa81ec10aae8dcaab098d82cb699ea66ada4ed09c794a37" + url: "https://pub.dev" + source: hosted + version: "0.1.0+1" + time: + dependency: transitive + description: + name: time + sha256: "83427e11d9072e038364a5e4da559e85869b227cf699a541be0da74f14140124" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + timezone: + dependency: "direct main" + description: + name: timezone + sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" + url: "https://pub.dev" + source: hosted + version: "0.9.2" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c + url: "https://pub.dev" + source: hosted + version: "6.2.4" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" + url: "https://pub.dev" + source: hosted + version: "6.2.2" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" + url: "https://pub.dev" + source: hosted + version: "6.2.4" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "4aca1e060978e19b2998ee28503f40b5ba6226819c2b5e3e4d1821e8ccd92198" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b + url: "https://pub.dev" + source: hosted + version: "2.2.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752" + url: "https://pub.dev" + source: hosted + version: "1.1.10+1" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33 + url: "https://pub.dev" + source: hosted + version: "1.1.10+1" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a" + url: "https://pub.dev" + source: hosted + version: "1.1.10+1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + video_player: + dependency: "direct main" + description: + name: video_player + sha256: fbf28ce8bcfe709ad91b5789166c832cb7a684d14f571a81891858fefb5bb1c2 + url: "https://pub.dev" + source: hosted + version: "2.8.2" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: f338a5a396c845f4632959511cad3542cdf3167e1b2a1a948ef07f7123c03608 + url: "https://pub.dev" + source: hosted + version: "2.4.9" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: "1ca9acd7a0fb15fb1a990cb554e6f004465c6f37c99d2285766f08a4b2802988" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: "44ce41424d104dfb7cf6982cc6b84af2b007a24d126406025bf40de5d481c74c" + url: "https://pub.dev" + source: hosted + version: "2.0.16" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" + wakelock_plus: + dependency: "direct main" + description: + name: wakelock_plus + sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d + url: "https://pub.dev" + source: hosted + version: "1.1.4" + wakelock_plus_platform_interface: + dependency: transitive + description: + name: wakelock_plus_platform_interface + sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + win32: + dependency: transitive + description: + name: win32 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: f0c26453a2d47aa4c2570c6a033246a3fc62da2fe23c7ffdd0a7495086dc0247 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + xxh3: + dependency: transitive + description: + name: xxh3 + sha256: a92b30944a9aeb4e3d4f3c3d4ddb3c7816ca73475cd603682c4f8149690f56d7 + url: "https://pub.dev" + source: hosted + version: "1.0.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0"