refactor(mobile): optimize padding and metrics handling in thumbnail tile widget

This commit is contained in:
idubnori
2025-12-19 01:31:10 +09:00
parent 5050f8fbb4
commit 501617ba93
@@ -55,8 +55,7 @@ class ThumbnailTile extends ConsumerWidget {
Container(color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor), Container(color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor),
LayoutBuilder( LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final horizontalPadding = constraints.maxWidth * 0.02; final metrics = _OverlayMetrics.fromConstraints(constraints);
final verticalPadding = constraints.maxHeight * 0.02;
return AnimatedContainer( return AnimatedContainer(
duration: Durations.short4, duration: Durations.short4,
@@ -81,11 +80,14 @@ class ThumbnailTile extends ConsumerWidget {
Align( Align(
alignment: Alignment.topRight, alignment: Alignment.topRight,
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(horizontal: horizontalPadding, vertical: verticalPadding), padding: EdgeInsets.symmetric(
horizontal: metrics.horizontalPadding,
vertical: metrics.verticalPadding,
),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [_AssetTypeIcons(asset: asset)], children: [_AssetTypeIcons(asset: asset, metrics: metrics)],
), ),
), ),
), ),
@@ -95,33 +97,36 @@ class ThumbnailTile extends ConsumerWidget {
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(horizontal: horizontalPadding, vertical: verticalPadding), padding: EdgeInsets.symmetric(
horizontal: metrics.horizontalPadding,
vertical: metrics.verticalPadding,
),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
if (asset != null && asset.isFavorite) if (asset != null && asset.isFavorite)
const Padding( Padding(
padding: EdgeInsets.only(right: 2.0), padding: EdgeInsets.only(right: metrics.iconSpacing),
child: _TileOverlayIcon(Icons.favorite_rounded), child: _TileOverlayIcon(Icons.favorite_rounded, metrics: metrics),
) )
else else
const SizedBox.shrink(), const SizedBox.shrink(),
if (shouldShowOwnerName) if (shouldShowOwnerName)
Flexible( Flexible(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 1.0), padding: EdgeInsets.symmetric(horizontal: metrics.iconSpacing),
child: _OwnerNameLabel(ownerName: ownerName!), child: _OwnerNameLabel(ownerName: ownerName!, metrics: metrics),
), ),
), ),
if (storageIndicator && asset != null) if (storageIndicator && asset != null)
Padding( Padding(
padding: const EdgeInsets.only(right: 2.0), padding: EdgeInsets.only(right: metrics.iconSpacing),
child: _TileOverlayIcon(switch (asset.storage) { child: _TileOverlayIcon(switch (asset.storage) {
AssetState.local => Icons.cloud_off_outlined, AssetState.local => Icons.cloud_off_outlined,
AssetState.remote => Icons.cloud_outlined, AssetState.remote => Icons.cloud_outlined,
AssetState.merged => Icons.cloud_done_outlined, AssetState.merged => Icons.cloud_done_outlined,
}), }, metrics: metrics),
), ),
], ],
), ),
@@ -182,7 +187,8 @@ class _SelectionIndicator extends StatelessWidget {
class _VideoIndicator extends StatelessWidget { class _VideoIndicator extends StatelessWidget {
final Duration duration; final Duration duration;
const _VideoIndicator(this.duration); final _OverlayMetrics metrics;
const _VideoIndicator(this.duration, {required this.metrics});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -194,15 +200,15 @@ class _VideoIndicator extends StatelessWidget {
children: [ children: [
Text( Text(
duration.format(), duration.format(),
style: const TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 12, fontSize: metrics.fontSize,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6))], shadows: [Shadow(blurRadius: metrics.blurRadius, color: const Color.fromRGBO(0, 0, 0, 0.6))],
), ),
), ),
const SizedBox(width: 2), SizedBox(width: metrics.iconSpacing),
const _TileOverlayIcon(Icons.play_circle_outline_rounded), _TileOverlayIcon(Icons.play_circle_outline_rounded, metrics: metrics),
], ],
); );
} }
@@ -210,24 +216,32 @@ class _VideoIndicator extends StatelessWidget {
class _TileOverlayIcon extends StatelessWidget { class _TileOverlayIcon extends StatelessWidget {
final IconData icon; final IconData icon;
final _OverlayMetrics metrics;
const _TileOverlayIcon(this.icon); const _TileOverlayIcon(this.icon, {required this.metrics});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Icon( return Icon(
icon, icon,
color: Colors.white, color: Colors.white,
size: 16, size: metrics.iconSize,
shadows: [const Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], shadows: [
Shadow(
blurRadius: metrics.blurRadius,
color: const Color.fromRGBO(0, 0, 0, 0.6),
offset: const Offset(0.0, 0.0),
),
],
); );
} }
} }
class _AssetTypeIcons extends StatelessWidget { class _AssetTypeIcons extends StatelessWidget {
final BaseAsset asset; final BaseAsset asset;
final _OverlayMetrics metrics;
const _AssetTypeIcons({required this.asset}); const _AssetTypeIcons({required this.asset, required this.metrics});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -237,11 +251,21 @@ class _AssetTypeIcons extends StatelessWidget {
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (asset.isVideo) Padding(padding: const EdgeInsets.only(left: 2.0), child: _VideoIndicator(asset.duration)), if (asset.isVideo)
Padding(
padding: EdgeInsets.only(left: metrics.iconSpacing),
child: _VideoIndicator(asset.duration, metrics: metrics),
),
if (hasStack) if (hasStack)
const Padding(padding: EdgeInsets.only(left: 2.0), child: _TileOverlayIcon(Icons.burst_mode_rounded)), Padding(
padding: EdgeInsets.only(left: metrics.iconSpacing),
child: _TileOverlayIcon(Icons.burst_mode_rounded, metrics: metrics),
),
if (isLivePhoto) if (isLivePhoto)
const Padding(padding: EdgeInsets.only(left: 2.0), child: _TileOverlayIcon(Icons.motion_photos_on_rounded)), Padding(
padding: EdgeInsets.only(left: metrics.iconSpacing),
child: _TileOverlayIcon(Icons.motion_photos_on_rounded, metrics: metrics),
),
], ],
); );
} }
@@ -249,18 +273,25 @@ class _AssetTypeIcons extends StatelessWidget {
class _OwnerNameLabel extends StatelessWidget { class _OwnerNameLabel extends StatelessWidget {
final String ownerName; final String ownerName;
final _OverlayMetrics metrics;
const _OwnerNameLabel({required this.ownerName}); const _OwnerNameLabel({required this.ownerName, required this.metrics});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Text( return Text(
ownerName, ownerName,
style: const TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 12, fontSize: metrics.fontSize,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], shadows: [
Shadow(
blurRadius: metrics.blurRadius,
color: const Color.fromRGBO(0, 0, 0, 0.6),
offset: const Offset(0.0, 0.0),
),
],
), ),
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
softWrap: false, softWrap: false,
@@ -268,3 +299,64 @@ class _OwnerNameLabel extends StatelessWidget {
); );
} }
} }
class _OverlayMetrics {
final double horizontalPadding;
final double verticalPadding;
final double iconSize;
final double fontSize;
final double iconSpacing;
final double blurRadius;
const _OverlayMetrics({
required this.horizontalPadding,
required this.verticalPadding,
required this.iconSize,
required this.fontSize,
required this.iconSpacing,
required this.blurRadius,
});
factory _OverlayMetrics.fromConstraints(BoxConstraints constraints) {
final tileSize = constraints.maxWidth;
if (tileSize < 80) {
return const _OverlayMetrics(
horizontalPadding: 1.0,
verticalPadding: 1.0,
iconSize: 14,
fontSize: 11,
iconSpacing: 1.0,
blurRadius: 3.0,
);
}
if (tileSize < 120) {
return const _OverlayMetrics(
horizontalPadding: 2.0,
verticalPadding: 2.0,
iconSize: 16,
fontSize: 12,
iconSpacing: 2.0,
blurRadius: 5.0,
);
}
if (tileSize < 180) {
return const _OverlayMetrics(
horizontalPadding: 3.0,
verticalPadding: 3.0,
iconSize: 18,
fontSize: 13,
iconSpacing: 3.0,
blurRadius: 6.0,
);
}
return const _OverlayMetrics(
horizontalPadding: 4.0,
verticalPadding: 4.0,
iconSize: 20,
fontSize: 14,
iconSpacing: 4.0,
blurRadius: 7.0,
);
}
}