123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- import 'package:auto_route/auto_route.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:immich_mobile/routing/router.dart';
- import 'package:immich_mobile/shared/models/asset.dart';
- import 'package:immich_mobile/shared/ui/immich_image.dart';
- import 'package:immich_mobile/utils/storage_indicator.dart';
- import 'package:isar/isar.dart';
- class ThumbnailImage extends StatelessWidget {
- final Asset asset;
- final int index;
- final Asset Function(int index) loadAsset;
- final int totalAssets;
- final bool showStorageIndicator;
- final bool showStack;
- final bool isOwner;
- final bool useGrayBoxPlaceholder;
- final bool isSelected;
- final bool multiselectEnabled;
- final Function? onSelect;
- final Function? onDeselect;
- final int heroOffset;
- final String? sharedAlbumId;
- const ThumbnailImage({
- Key? key,
- required this.asset,
- required this.index,
- required this.loadAsset,
- required this.totalAssets,
- this.showStorageIndicator = true,
- this.showStack = false,
- this.isOwner = true,
- this.sharedAlbumId,
- this.useGrayBoxPlaceholder = false,
- this.isSelected = false,
- this.multiselectEnabled = false,
- this.onDeselect,
- this.onSelect,
- this.heroOffset = 0,
- }) : super(key: key);
- @override
- Widget build(BuildContext context) {
- final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
- final assetContainerColor =
- isDarkTheme ? Colors.blueGrey : Theme.of(context).primaryColorLight;
- // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
- final isFromDto = asset.id == Isar.autoIncrement;
- Widget buildSelectionIcon(Asset asset) {
- if (isSelected) {
- return Container(
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: assetContainerColor,
- ),
- child: Icon(
- Icons.check_circle_rounded,
- color: Theme.of(context).primaryColor,
- ),
- );
- } else {
- return const Icon(
- Icons.circle_outlined,
- color: Colors.white,
- );
- }
- }
- Widget buildVideoIcon() {
- final minutes = asset.duration.inMinutes;
- final durationString = asset.duration.toString();
- return Positioned(
- top: 5,
- right: 8,
- child: Row(
- children: [
- Text(
- minutes > 59
- ? durationString.substring(0, 7) // h:mm:ss
- : minutes > 0
- ? durationString.substring(2, 7) // mm:ss
- : durationString.substring(3, 7), // m:ss
- style: const TextStyle(
- color: Colors.white,
- fontSize: 10,
- fontWeight: FontWeight.bold,
- ),
- ),
- const SizedBox(
- width: 3,
- ),
- const Icon(
- Icons.play_circle_fill_rounded,
- color: Colors.white,
- size: 18,
- ),
- ],
- ),
- );
- }
- Widget buildStackIcon() {
- return Positioned(
- top: !asset.isImage ? 28 : 5,
- right: 8,
- child: Row(
- children: [
- if (asset.stackChildrenCount > 1)
- Text(
- "${asset.stackChildrenCount}",
- style: const TextStyle(
- color: Colors.white,
- fontSize: 10,
- fontWeight: FontWeight.bold,
- ),
- ),
- if (asset.stackChildrenCount > 1)
- const SizedBox(
- width: 3,
- ),
- const Icon(
- Icons.burst_mode_rounded,
- color: Colors.white,
- size: 18,
- ),
- ],
- ),
- );
- }
- Widget buildImage() {
- final image = SizedBox(
- width: 300,
- height: 300,
- child: Hero(
- tag: isFromDto
- ? '${asset.remoteId}-$heroOffset'
- : asset.id + heroOffset,
- child: ImmichImage(
- asset,
- useGrayBoxPlaceholder: useGrayBoxPlaceholder,
- fit: BoxFit.cover,
- ),
- ),
- );
- if (!multiselectEnabled || !isSelected) {
- return image;
- }
- return Container(
- decoration: BoxDecoration(
- border: Border.all(
- width: 0,
- color: onDeselect == null ? Colors.grey : assetContainerColor,
- ),
- color: onDeselect == null ? Colors.grey : assetContainerColor,
- ),
- child: ClipRRect(
- borderRadius: const BorderRadius.only(
- topRight: Radius.circular(15.0),
- bottomRight: Radius.circular(15.0),
- bottomLeft: Radius.circular(15.0),
- topLeft: Radius.zero,
- ),
- child: image,
- ),
- );
- }
- return GestureDetector(
- onTap: () {
- if (multiselectEnabled) {
- if (isSelected) {
- onDeselect?.call();
- } else {
- onSelect?.call();
- }
- } else {
- AutoRouter.of(context).push(
- GalleryViewerRoute(
- initialIndex: index,
- loadAsset: loadAsset,
- totalAssets: totalAssets,
- heroOffset: heroOffset,
- showStack: showStack,
- isOwner: isOwner,
- sharedAlbumId: sharedAlbumId,
- ),
- );
- }
- },
- onLongPress: () {
- onSelect?.call();
- HapticFeedback.heavyImpact();
- },
- child: Stack(
- children: [
- Container(
- decoration: BoxDecoration(
- border: multiselectEnabled && isSelected
- ? Border.all(
- color: onDeselect == null
- ? Colors.grey
- : assetContainerColor,
- width: 8,
- )
- : const Border(),
- ),
- child: buildImage(),
- ),
- if (multiselectEnabled)
- Padding(
- padding: const EdgeInsets.all(3.0),
- child: Align(
- alignment: Alignment.topLeft,
- child: buildSelectionIcon(asset),
- ),
- ),
- if (showStorageIndicator)
- Positioned(
- right: 8,
- bottom: 5,
- child: Icon(
- storageIcon(asset),
- color: Colors.white,
- size: 18,
- ),
- ),
- if (asset.isFavorite)
- const Positioned(
- left: 8,
- bottom: 5,
- child: Icon(
- Icons.favorite,
- color: Colors.white,
- size: 18,
- ),
- ),
- if (!asset.isImage) buildVideoIcon(),
- if (asset.stackChildrenCount > 0) buildStackIcon(),
- ],
- ),
- );
- }
- }
|