feat(mobile): optimized thumbnail widget (#21073)

* thumbnail widget

* use animation ticker, improvements

* use static thumbnail resolution for now

* fix android sample size

* free memory sooner

* formatting

* tweaks

* wait for disposal

* remove debug prints

* take two on animation

* fix

* remote constructor

* missed one

* unused imports

* unnecessary import

* formatting
This commit is contained in:
Mert 2025-08-21 14:06:02 -04:00 committed by GitHub
parent ab2849781a
commit fb59fa343d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 421 additions and 125 deletions

View file

@ -7,13 +7,14 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/duration_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
class ThumbnailTile extends ConsumerWidget {
const ThumbnailTile(
this.asset, {
this.size = const Size.square(256),
this.size = kThumbnailResolution,
this.fit = BoxFit.cover,
this.showStorageIndicator,
this.lockSelection = false,
@ -21,7 +22,7 @@ class ThumbnailTile extends ConsumerWidget {
super.key,
});
final BaseAsset asset;
final BaseAsset? asset;
final Size size;
final BoxFit fit;
final bool? showStorageIndicator;
@ -30,6 +31,7 @@ class ThumbnailTile extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final asset = this.asset;
final heroIndex = heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0;
final assetContainerColor = context.isDarkTheme
@ -52,7 +54,7 @@ class ThumbnailTile extends ConsumerWidget {
)
: const BoxDecoration();
final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null;
final hasStack = asset is RemoteAsset && asset.stackId != null;
final bool storageIndicator =
showStorageIndicator ?? ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator)));
@ -71,8 +73,8 @@ class ThumbnailTile extends ConsumerWidget {
children: [
Positioned.fill(
child: Hero(
tag: '${asset.heroTag}_$heroIndex',
child: Thumbnail(asset: asset, fit: fit, size: size),
tag: '${asset?.heroTag ?? ''}_$heroIndex',
child: Thumbnail.fromAsset(asset: asset, size: size),
),
),
if (hasStack)
@ -83,7 +85,7 @@ class ThumbnailTile extends ConsumerWidget {
child: const _TileOverlayIcon(Icons.burst_mode_rounded),
),
),
if (asset.isVideo)
if (asset != null && asset.isVideo)
Align(
alignment: Alignment.topRight,
child: Padding(
@ -91,7 +93,7 @@ class ThumbnailTile extends ConsumerWidget {
child: _VideoIndicator(asset.duration),
),
),
if (storageIndicator)
if (storageIndicator && asset != null)
switch (asset.storage) {
AssetState.local => const Align(
alignment: Alignment.bottomRight,
@ -115,7 +117,7 @@ class ThumbnailTile extends ConsumerWidget {
),
),
},
if (asset.isFavorite)
if (asset != null && asset.isFavorite)
const Align(
alignment: Alignment.bottomLeft,
child: Padding(