feat: improvements of thumbnail animations (#20300)

* feat: improve thumbnail border radius animation

feat: remove thin border between image and image selection container

feat: enhance selection icon in thumbnail image

feat: add animated selection indicator for multiselect in thumbnail image

feat: remove unnecessary widgets and variables

style: format code

fix: errors with formatting checks

* chore: port to new timeline

* chore: revert mobile/lib/widgets/asset_grid/thumbnail_image.dart

---------

Co-authored-by: bwees <brandonwees@gmail.com>
This commit is contained in:
Lauritz Tieste 2025-10-24 05:36:49 +02:00 committed by GitHub
parent 6164b027e2
commit 34bad1ce71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -42,31 +42,23 @@ class ThumbnailTile extends ConsumerWidget {
multiSelectProvider.select((multiselect) => multiselect.selectedAssets.contains(asset)), multiSelectProvider.select((multiselect) => multiselect.selectedAssets.contains(asset)),
); );
final borderStyle = lockSelection
? BoxDecoration(
color: context.colorScheme.surfaceContainerHighest,
border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 6),
)
: isSelected
? BoxDecoration(
color: assetContainerColor,
border: Border.all(color: assetContainerColor, width: 6),
)
: const BoxDecoration();
final bool storageIndicator = final bool storageIndicator =
ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator))) && showStorageIndicator; ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator))) && showStorageIndicator;
return Stack( return Stack(
children: [ children: [
Container(color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor),
AnimatedContainer( AnimatedContainer(
duration: Durations.short4, duration: Durations.short4,
curve: Curves.decelerate, curve: Curves.decelerate,
decoration: borderStyle, padding: EdgeInsets.all(isSelected || lockSelection ? 6 : 0),
child: ClipRRect( child: TweenAnimationBuilder<double>(
borderRadius: isSelected || lockSelection tween: Tween<double>(begin: 0.0, end: (isSelected || lockSelection) ? 15.0 : 0.0),
? const BorderRadius.all(Radius.circular(15.0)) duration: Durations.short4,
: BorderRadius.zero, curve: Curves.decelerate,
builder: (context, value, child) {
return ClipRRect(borderRadius: BorderRadius.all(Radius.circular(value)), child: child);
},
child: Stack( child: Stack(
children: [ children: [
Positioned.fill( Positioned.fill(
@ -116,29 +108,36 @@ class ThumbnailTile extends ConsumerWidget {
), ),
), ),
), ),
if (isSelected || lockSelection) TweenAnimationBuilder<double>(
Padding( tween: Tween<double>(begin: 0.0, end: (isSelected || lockSelection) ? 1.0 : 0.0),
padding: const EdgeInsets.all(3.0), duration: Durations.short4,
child: Align( curve: Curves.decelerate,
alignment: Alignment.topLeft, builder: (context, value, child) {
child: _SelectionIndicator( return Padding(
isSelected: isSelected, padding: EdgeInsets.all((isSelected || lockSelection) ? value * 3.0 : 3.0),
isLocked: lockSelection, child: Align(
color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor, alignment: Alignment.topLeft,
child: Opacity(
opacity: (isSelected || lockSelection) ? 1 : value,
child: _SelectionIndicator(
isLocked: lockSelection,
color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor,
),
),
), ),
), );
), },
),
], ],
); );
} }
} }
class _SelectionIndicator extends StatelessWidget { class _SelectionIndicator extends StatelessWidget {
final bool isSelected;
final bool isLocked; final bool isLocked;
final Color? color; final Color? color;
const _SelectionIndicator({required this.isSelected, required this.isLocked, this.color}); const _SelectionIndicator({required this.isLocked, this.color});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -147,13 +146,11 @@ class _SelectionIndicator extends StatelessWidget {
decoration: BoxDecoration(shape: BoxShape.circle, color: color), decoration: BoxDecoration(shape: BoxShape.circle, color: color),
child: const Icon(Icons.check_circle_rounded, color: Colors.grey), child: const Icon(Icons.check_circle_rounded, color: Colors.grey),
); );
} else if (isSelected) { } else {
return DecoratedBox( return DecoratedBox(
decoration: BoxDecoration(shape: BoxShape.circle, color: color), decoration: BoxDecoration(shape: BoxShape.circle, color: color),
child: Icon(Icons.check_circle_rounded, color: context.primaryColor), child: Icon(Icons.check_circle_rounded, color: context.primaryColor),
); );
} else {
return const Icon(Icons.circle_outlined, color: Colors.white);
} }
} }
} }