diff --git a/lib/model/filters/query.dart b/lib/model/filters/query.dart index b44eb14a9..fd336b271 100644 --- a/lib/model/filters/query.dart +++ b/lib/model/filters/query.dart @@ -6,12 +6,12 @@ import 'package:outline_material_icons/outline_material_icons.dart'; class QueryFilter extends CollectionFilter { static const type = 'query'; - final String query; + final String query, upQuery; - const QueryFilter(this.query); + QueryFilter(this.query) : upQuery = query.toUpperCase(); @override - bool filter(ImageEntry entry) => entry.search(query); + bool filter(ImageEntry entry) => entry.search(upQuery); @override bool get isUnique => false; @@ -32,5 +32,5 @@ class QueryFilter extends CollectionFilter { } @override - int get hashCode => hashValues('MetadataFilter', query); + int get hashCode => hashValues('QueryFilter', query); } diff --git a/lib/model/image_entry.dart b/lib/model/image_entry.dart index c672eb617..9dcc42bb5 100644 --- a/lib/model/image_entry.dart +++ b/lib/model/image_entry.dart @@ -205,9 +205,9 @@ class ImageEntry { } bool search(String query) { - if (title.toLowerCase().contains(query)) return true; - if (catalogMetadata?.xmpSubjects?.toLowerCase()?.contains(query) ?? false) return true; - if (isLocated && addressDetails.addressLine.toLowerCase().contains(query)) return true; + if (title?.toUpperCase()?.contains(query) ?? false) return true; + if (catalogMetadata?.xmpSubjects?.toUpperCase()?.contains(query) ?? false) return true; + if (addressDetails?.addressLine?.toUpperCase()?.contains(query) ?? false) return true; return false; } diff --git a/lib/widgets/album/search/expandable_filter_row.dart b/lib/widgets/album/search/expandable_filter_row.dart index fd95fae34..1dfbb7758 100644 --- a/lib/widgets/album/search/expandable_filter_row.dart +++ b/lib/widgets/album/search/expandable_filter_row.dart @@ -1,6 +1,5 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/utils/constants.dart'; -import 'package:aves/widgets/album/search/search_delegate.dart'; import 'package:aves/widgets/common/aves_filter_chip.dart'; import 'package:flutter/material.dart'; import 'package:outline_material_icons/outline_material_icons.dart'; @@ -47,45 +46,62 @@ class ExpandableFilterRow extends StatelessWidget { } final filtersList = filters.toList(); - final filterChips = isExpanded - ? Padding( - padding: const EdgeInsets.symmetric(horizontal: AvesFilterChip.buttonBorderWidth / 2 + 6), - child: Wrap( - spacing: 8, - children: filtersList - .map((filter) => AvesFilterChip( - filter: filter, - onPressed: onPressed, - )) - .toList(), - ), - ) - : Container( - height: kMinInteractiveDimension, - child: ListView.separated( - scrollDirection: Axis.horizontal, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.all(AvesFilterChip.buttonBorderWidth / 2) + const EdgeInsets.symmetric(horizontal: 6), - itemBuilder: (context, index) { - if (index >= filtersList.length) return null; - final filter = filtersList[index]; - return Center( - child: AvesFilterChip( - filter: filter, - onPressed: onPressed, - ), - ); - }, - separatorBuilder: (context, index) => const SizedBox(width: 8), - itemCount: filtersList.length, + final wrap = Container( + key: ValueKey('wrap$title'), + padding: const EdgeInsets.symmetric(horizontal: AvesFilterChip.buttonBorderWidth / 2 + 6), + // specify transparent as a workaround to prevent + // chip border clipping when the floating app bar is fading + color: Colors.transparent, + child: Wrap( + spacing: 8, + children: filtersList + .map((filter) => AvesFilterChip( + filter: filter, + onPressed: onPressed, + )) + .toList(), + ), + ); + final list = Container( + key: ValueKey('list$title'), + // specify transparent as a workaround to prevent + // chip border clipping when the floating app bar is fading + color: Colors.transparent, + height: kMinInteractiveDimension, + child: ListView.separated( + scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(AvesFilterChip.buttonBorderWidth / 2) + const EdgeInsets.symmetric(horizontal: 6), + itemBuilder: (context, index) { + if (index >= filtersList.length) return null; + final filter = filtersList[index]; + return Center( + child: AvesFilterChip( + filter: filter, + onPressed: onPressed, ), ); + }, + separatorBuilder: (context, index) => const SizedBox(width: 8), + itemCount: filtersList.length, + ), + ); + final filterChips = isExpanded ? wrap : list; return titleRow != null ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ titleRow, - filterChips, + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: filterChips, + layoutBuilder: (currentChild, previousChildren) => Stack( + children: [ + ...previousChildren, + if (currentChild != null) currentChild, + ], + ), + ), ], ) : filterChips; diff --git a/lib/widgets/album/search/search_delegate.dart b/lib/widgets/album/search/search_delegate.dart index 1a32b678d..e24ab55d7 100644 --- a/lib/widgets/album/search/search_delegate.dart +++ b/lib/widgets/album/search/search_delegate.dart @@ -53,7 +53,7 @@ class ImageSearchDelegate extends SearchDelegate { @override Widget buildSuggestions(BuildContext context) { final source = collection.source; - final upQuery = query.toUpperCase(); + final upQuery = query.trim().toUpperCase(); final containQuery = (String s) => s.toUpperCase().contains(upQuery); return SafeArea( child: ValueListenableBuilder( @@ -98,7 +98,8 @@ class ImageSearchDelegate extends SearchDelegate { @override Widget buildResults(BuildContext context) { - close(context, QueryFilter(query)); + final cleanQuery = query.trim(); + close(context, cleanQuery.isNotEmpty ? QueryFilter(cleanQuery) : null); return const SizedBox.shrink(); } }