diff --git a/mobile/lib/widgets/search/curated_people_row.dart b/mobile/lib/widgets/search/curated_people_row.dart index 897cd454f6b01..f8e4fbe3c0655 100644 --- a/mobile/lib/widgets/search/curated_people_row.dart +++ b/mobile/lib/widgets/search/curated_people_row.dart @@ -26,7 +26,7 @@ class CuratedPeopleRow extends StatelessWidget { @override Widget build(BuildContext context) { return SizedBox( - height: imageSize + 30, + height: imageSize + 50, child: ListView.separated( padding: padding, scrollDirection: Axis.horizontal, @@ -57,7 +57,10 @@ class CuratedPeopleRow extends StatelessWidget { ), ), const SizedBox(height: 8), - _buildPersonLabel(context, person, index), + SizedBox( + width: imageSize, + child: _buildPersonLabel(context, person, index), + ), ], ); }, @@ -79,6 +82,9 @@ class CuratedPeopleRow extends StatelessWidget { style: context.textTheme.labelLarge?.copyWith( color: context.primaryColor, ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, ).tr(), ); } @@ -87,6 +93,7 @@ class CuratedPeopleRow extends StatelessWidget { textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: context.textTheme.labelLarge, + maxLines: 2, ); } } diff --git a/mobile/lib/widgets/search/curated_places_row.dart b/mobile/lib/widgets/search/curated_places_row.dart index 4488f9cb7d189..c4cfdedc279ab 100644 --- a/mobile/lib/widgets/search/curated_places_row.dart +++ b/mobile/lib/widgets/search/curated_places_row.dart @@ -28,32 +28,30 @@ class CuratedPlacesRow extends StatelessWidget { return SizedBox( height: imageSize, - child: ListView.builder( + child: ListView.separated( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric( horizontal: 16, ), + separatorBuilder: (context, index) => const SizedBox(width: 10), itemBuilder: (context, index) { // Injecting Map thumbnail as the first element if (isMapEnabled && index == 0) { - return SearchMapThumbnail( - size: imageSize, + return SizedBox.square( + dimension: imageSize, + child: SearchMapThumbnail(size: imageSize), ); } final actualIndex = index - actualContentIndex; final object = content[actualIndex]; final thumbnailRequestUrl = '${Store.get(StoreKey.serverEndpoint)}/assets/${object.id}/thumbnail'; - return SizedBox( - width: imageSize, - height: imageSize, - child: Padding( - padding: const EdgeInsets.only(right: 10.0), - child: ThumbnailWithInfo( - imageUrl: thumbnailRequestUrl, - textInfo: object.label, - onTap: () => onTap?.call(object, actualIndex), - ), + return SizedBox.square( + dimension: imageSize, + child: ThumbnailWithInfo( + imageUrl: thumbnailRequestUrl, + textInfo: object.label, + onTap: () => onTap?.call(object, actualIndex), ), ); }, diff --git a/mobile/lib/widgets/search/search_map_thumbnail.dart b/mobile/lib/widgets/search/search_map_thumbnail.dart index f0c36a8192995..20747913fb14a 100644 --- a/mobile/lib/widgets/search/search_map_thumbnail.dart +++ b/mobile/lib/widgets/search/search_map_thumbnail.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; +import 'package:immich_mobile/widgets/search/thumbnail_with_info_container.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; class SearchMapThumbnail extends StatelessWidget { @@ -15,60 +16,21 @@ class SearchMapThumbnail extends StatelessWidget { @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () => context.pushRoute( - const MapRoute(), - ), - child: SizedBox.square( - dimension: size, - child: Stack( - children: [ - Padding( - padding: const EdgeInsets.only(right: 10.0), - child: MapThumbnail( - zoom: 2, - centre: const LatLng( - 47, - 5, - ), - height: size, - width: size, - showAttribution: false, - ), - ), - Padding( - padding: const EdgeInsets.only(right: 10.0), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.black, - gradient: LinearGradient( - begin: FractionalOffset.topCenter, - end: FractionalOffset.bottomCenter, - colors: [ - Colors.blueGrey.withOpacity(0.0), - Colors.black.withOpacity(0.4), - ], - stops: const [0.0, 0.4], - ), - ), - ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.only(bottom: 10), - child: const Text( - "search_page_your_map", - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14, - ), - ).tr(), - ), - ), - ], + return ThumbnailWithInfoContainer( + label: 'search_page_your_map'.tr(), + onTap: () { + context.pushRoute(const MapRoute()); + }, + child: IgnorePointer( + child: MapThumbnail( + zoom: 2, + centre: const LatLng( + 47, + 5, + ), + height: size, + width: size, + showAttribution: false, ), ), ); diff --git a/mobile/lib/widgets/search/thumbnail_with_info.dart b/mobile/lib/widgets/search/thumbnail_with_info.dart index d2925e6e3090c..035bc4420f103 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info.dart @@ -2,94 +2,53 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/extensions/string_extensions.dart'; +import 'package:immich_mobile/widgets/search/thumbnail_with_info_container.dart'; -// ignore: must_be_immutable class ThumbnailWithInfo extends StatelessWidget { - ThumbnailWithInfo({ + const ThumbnailWithInfo({ super.key, required this.textInfo, this.imageUrl, this.noImageIcon, this.borderRadius = 10, - required this.onTap, + this.onTap, }); final String textInfo; final String? imageUrl; - final Function onTap; + final VoidCallback? onTap; final IconData? noImageIcon; - double borderRadius; + final double borderRadius; @override Widget build(BuildContext context) { var textAndIconColor = context.isDarkTheme ? Colors.grey[100] : Colors.grey[700]; - return GestureDetector( - onTap: () { - onTap(); - }, - child: Stack( - alignment: Alignment.bottomCenter, - children: [ - Container( - decoration: BoxDecoration( + return ThumbnailWithInfoContainer( + onTap: onTap, + borderRadius: borderRadius, + label: textInfo, + child: imageUrl != null + ? ClipRRect( borderRadius: BorderRadius.circular(borderRadius), - color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[100], - ), - child: imageUrl != null - ? ClipRRect( - borderRadius: BorderRadius.circular(borderRadius), - child: CachedNetworkImage( - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - imageUrl: imageUrl!, - httpHeaders: { - "x-immich-user-token": Store.get(StoreKey.accessToken), - }, - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), - ), - ) - : Center( - child: Icon( - noImageIcon ?? Icons.not_listed_location, - color: textAndIconColor, - ), - ), - ), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(borderRadius), - color: Colors.white, - gradient: LinearGradient( - begin: FractionalOffset.topCenter, - end: FractionalOffset.bottomCenter, - colors: [ - Colors.grey.withOpacity(0.0), - textInfo == '' - ? Colors.black.withOpacity(0.1) - : Colors.black.withOpacity(0.5), - ], - stops: const [0.0, 1.0], + child: CachedNetworkImage( + width: double.infinity, + height: double.infinity, + fit: BoxFit.cover, + imageUrl: imageUrl!, + httpHeaders: { + "x-immich-user-token": Store.get(StoreKey.accessToken), + }, + errorWidget: (context, url, error) => + const Icon(Icons.image_not_supported_outlined), ), - ), - ), - Positioned( - bottom: 12, - left: 14, - child: Text( - textInfo == '' ? textInfo : textInfo.capitalize(), - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14, + ) + : Center( + child: Icon( + noImageIcon ?? Icons.not_listed_location, + color: textAndIconColor, ), ), - ), - ], - ), ); } } diff --git a/mobile/lib/widgets/search/thumbnail_with_info_container.dart b/mobile/lib/widgets/search/thumbnail_with_info_container.dart new file mode 100644 index 0000000000000..6df45ec46480e --- /dev/null +++ b/mobile/lib/widgets/search/thumbnail_with_info_container.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class ThumbnailWithInfoContainer extends StatelessWidget { + const ThumbnailWithInfoContainer({ + super.key, + this.onTap, + this.borderRadius = 10, + required this.label, + required this.child, + }); + + final VoidCallback? onTap; + final double borderRadius; + final String label; + final Widget child; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(borderRadius), + color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[100], + ), + foregroundDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(borderRadius), + color: Colors.white, + gradient: LinearGradient( + begin: FractionalOffset.topCenter, + end: FractionalOffset.bottomCenter, + colors: [ + Colors.grey.withOpacity(0.0), + label == '' + ? Colors.black.withOpacity(0.1) + : Colors.black.withOpacity(0.5), + ], + stops: const [0.0, 1.0], + ), + ), + child: child, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8) + + const EdgeInsets.only(bottom: 8), + child: Text( + label, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 14, + ), + maxLines: 2, + softWrap: false, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } +}