diff --git a/app/lib/widget/collection_list_item.dart b/app/lib/widget/collection_list_item.dart new file mode 100644 index 00000000..387997d1 --- /dev/null +++ b/app/lib/widget/collection_list_item.dart @@ -0,0 +1,122 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart'; +import 'package:flutter/material.dart'; +import 'package:nc_photos/account.dart'; +import 'package:nc_photos/api/api.dart'; +import 'package:nc_photos/cache_manager_util.dart'; +import 'package:nc_photos/theme.dart'; + +class CollectionListSmall extends StatelessWidget { + const CollectionListSmall({ + Key? key, + required this.account, + required this.label, + required this.coverUrl, + required this.fallbackBuilder, + this.onTap, + }) : super(key: key); + + @override + build(BuildContext context) { + Widget content = Stack( + children: [ + SizedBox.expand( + child: _buildCoverImage(context), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 24, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withOpacity(0), + Colors.black.withOpacity(.55), + ], + ), + ), + ), + Container( + color: Colors.black.withOpacity(.55), + constraints: const BoxConstraints(minWidth: double.infinity), + padding: const EdgeInsets.fromLTRB(8, 0, 8, 4), + child: Text( + label, + style: TextStyle( + color: AppTheme.primaryTextColorDark, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + ], + ); + if (onTap != null) { + content = Stack( + fit: StackFit.expand, + children: [ + content, + Positioned.fill( + child: Material( + type: MaterialType.transparency, + child: InkWell( + onTap: onTap, + ), + ), + ), + ], + ); + } + return Container( + color: AppTheme.getListItemBackgroundColor(context), + constraints: const BoxConstraints.expand(), + child: content, + ); + } + + Widget _buildCoverImage(BuildContext context) { + Widget buildPlaceholder() => Padding( + padding: const EdgeInsets.all(4), + child: fallbackBuilder(context), + ); + try { + return FittedBox( + clipBehavior: Clip.hardEdge, + fit: BoxFit.cover, + child: CachedNetworkImage( + cacheManager: ThumbnailCacheManager.inst, + imageUrl: coverUrl, + httpHeaders: { + "Authorization": Api.getAuthorizationHeaderValue(account), + }, + fadeInDuration: const Duration(), + filterQuality: FilterQuality.high, + errorWidget: (context, url, error) => buildPlaceholder(), + imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, + ), + ); + } catch (_) { + return FittedBox( + child: buildPlaceholder(), + ); + } + } + + final Account account; + final String label; + final String coverUrl; + final Widget Function(BuildContext context) fallbackBuilder; + final VoidCallback? onTap; +} diff --git a/app/lib/widget/people_browser.dart b/app/lib/widget/people_browser.dart index 6bd54505..f710d1b9 100644 --- a/app/lib/widget/people_browser.dart +++ b/app/lib/widget/people_browser.dart @@ -1,5 +1,3 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -7,11 +5,9 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:kiwi/kiwi.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; -import 'package:nc_photos/api/api.dart'; import 'package:nc_photos/api/api_util.dart' as api_util; import 'package:nc_photos/app_localizations.dart'; import 'package:nc_photos/bloc/list_person.dart'; -import 'package:nc_photos/cache_manager_util.dart'; import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/entity/person.dart'; import 'package:nc_photos/exception.dart'; @@ -19,6 +15,7 @@ import 'package:nc_photos/exception_util.dart' as exception_util; import 'package:nc_photos/k.dart' as k; import 'package:nc_photos/snack_bar_manager.dart'; import 'package:nc_photos/theme.dart'; +import 'package:nc_photos/widget/collection_list_item.dart'; import 'package:nc_photos/widget/person_browser.dart'; class PeopleBrowserArguments { @@ -110,8 +107,8 @@ class _PeopleBrowserState extends State { ), SliverStaggeredGrid.extentBuilder( maxCrossAxisExtent: 160, - mainAxisSpacing: 8, - crossAxisSpacing: 8, + mainAxisSpacing: 2, + crossAxisSpacing: 2, itemCount: _items.length, itemBuilder: _buildItem, staggeredTileBuilder: (_) => const StaggeredTile.count(1, 1), @@ -205,111 +202,18 @@ class _PersonListItem extends _ListItem { }) : super(onTap: onTap); @override - buildWidget(BuildContext context) { - Widget content = Stack( - children: [ - _buildFaceImage(context), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - height: 24, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.black.withOpacity(0), - Colors.black.withOpacity(.55), - ], - ), - ), - ), - Container( - color: Colors.black.withOpacity(.55), - constraints: const BoxConstraints(minWidth: double.infinity), - padding: const EdgeInsets.fromLTRB(8, 0, 8, 4), - child: Text( - name, - style: TextStyle( - color: AppTheme.primaryTextColorDark, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ), - ], - ); - if (onTap != null) { - content = Stack( - fit: StackFit.expand, - children: [ - content, - Positioned.fill( - child: Material( - type: MaterialType.transparency, - child: InkWell( - onTap: onTap, - ), - ), - ), - ], - ); - } - return Padding( - padding: const EdgeInsets.all(4), - child: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: Container( - color: AppTheme.getListItemBackgroundColor(context), - constraints: const BoxConstraints.expand(), - child: content, - ), - ), - ); - } - - Widget _buildFaceImage(BuildContext context) { - try { - return FittedBox( - clipBehavior: Clip.hardEdge, - fit: BoxFit.cover, - child: CachedNetworkImage( - cacheManager: ThumbnailCacheManager.inst, - imageUrl: faceUrl!, - httpHeaders: { - "Authorization": Api.getAuthorizationHeaderValue(account), - }, - fadeInDuration: const Duration(), - filterQuality: FilterQuality.high, - errorWidget: (context, url, error) { - // just leave it empty - return Container(); - }, - imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, - ), - ); - } catch (_) { - return Center( - child: Icon( + buildWidget(BuildContext context) => CollectionListSmall( + account: account, + label: name, + coverUrl: faceUrl, + fallbackBuilder: (_) => Icon( Icons.person, color: Colors.white.withOpacity(.8), - size: 80, ), + onTap: onTap, ); - } - } final Account account; final String name; - final String? faceUrl; + final String faceUrl; }