fix(car_card): cache signed URL future in state to prevent rebuild flashing
Co-authored-by: derkauzigekoala <79001016+derkauzigekoala@users.noreply.github.com>
This commit is contained in:
parent
18af2d1bf2
commit
587cd6c739
1 changed files with 49 additions and 21 deletions
|
|
@ -4,7 +4,7 @@ import '../services/storage_service.dart';
|
||||||
import '../theme/app_colors.dart';
|
import '../theme/app_colors.dart';
|
||||||
|
|
||||||
/// A styled card for displaying a single die-cast car in the garage.
|
/// A styled card for displaying a single die-cast car in the garage.
|
||||||
class CarCard extends StatelessWidget {
|
class CarCard extends StatefulWidget {
|
||||||
final String hwId;
|
final String hwId;
|
||||||
final String? name;
|
final String? name;
|
||||||
final String? series;
|
final String? series;
|
||||||
|
|
@ -36,22 +36,50 @@ class CarCard extends StatelessWidget {
|
||||||
this.isSelected = false,
|
this.isSelected = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CarCard> createState() => _CarCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CarCardState extends State<CarCard> {
|
||||||
|
Future<String?>? _signedUrlFuture;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_initSignedUrlFuture();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(CarCard oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.imagePath != widget.imagePath) {
|
||||||
|
_initSignedUrlFuture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initSignedUrlFuture() {
|
||||||
|
final hasImagePath = widget.imagePath != null && widget.imagePath!.isNotEmpty;
|
||||||
|
_signedUrlFuture = hasImagePath
|
||||||
|
? StorageService.createSignedUrl(widget.imagePath!)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final isDark = theme.brightness == Brightness.dark;
|
final isDark = theme.brightness == Brightness.dark;
|
||||||
final resolvedImageUrl = imageUrl;
|
final resolvedImageUrl = widget.imageUrl;
|
||||||
final hasResolvedUrl = resolvedImageUrl != null && resolvedImageUrl.isNotEmpty;
|
final hasResolvedUrl = resolvedImageUrl != null && resolvedImageUrl.isNotEmpty;
|
||||||
final hasImagePath = imagePath != null && imagePath!.isNotEmpty;
|
final hasImagePath = widget.imagePath != null && widget.imagePath!.isNotEmpty;
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
color: isSelected
|
color: widget.isSelected
|
||||||
? AppColors.orange.withValues(alpha: 0.18)
|
? AppColors.orange.withValues(alpha: 0.18)
|
||||||
: Theme.of(context).cardTheme.color,
|
: Theme.of(context).cardTheme.color,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: widget.onTap,
|
||||||
onLongPress: onLongPress,
|
onLongPress: widget.onLongPress,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
@ -79,13 +107,13 @@ class CarCard extends StatelessWidget {
|
||||||
fadeInDuration: Duration.zero,
|
fadeInDuration: Duration.zero,
|
||||||
fadeOutDuration: Duration.zero,
|
fadeOutDuration: Duration.zero,
|
||||||
errorWidget: (context, url, error) {
|
errorWidget: (context, url, error) {
|
||||||
onImageError?.call();
|
widget.onImageError?.call();
|
||||||
return _PlaceholderIcon(isDark: isDark);
|
return _PlaceholderIcon(isDark: isDark);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: hasImagePath
|
: hasImagePath
|
||||||
? FutureBuilder<String?>(
|
? FutureBuilder<String?>(
|
||||||
future: StorageService.createSignedUrl(imagePath),
|
future: _signedUrlFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final signedUrl = snapshot.data;
|
final signedUrl = snapshot.data;
|
||||||
if (signedUrl == null || signedUrl.isEmpty) {
|
if (signedUrl == null || signedUrl.isEmpty) {
|
||||||
|
|
@ -97,7 +125,7 @@ class CarCard extends StatelessWidget {
|
||||||
fadeInDuration: Duration.zero,
|
fadeInDuration: Duration.zero,
|
||||||
fadeOutDuration: Duration.zero,
|
fadeOutDuration: Duration.zero,
|
||||||
errorWidget: (context, url, error) {
|
errorWidget: (context, url, error) {
|
||||||
onImageError?.call();
|
widget.onImageError?.call();
|
||||||
return _PlaceholderIcon(isDark: isDark);
|
return _PlaceholderIcon(isDark: isDark);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -113,7 +141,7 @@ class CarCard extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (isSelected)
|
if (widget.isSelected)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 6),
|
padding: const EdgeInsets.only(bottom: 6),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -141,7 +169,7 @@ class CarCard extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
hwId,
|
widget.hwId,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
|
|
@ -154,19 +182,19 @@ class CarCard extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
isVerified
|
widget.isVerified
|
||||||
? Icons.verified
|
? Icons.verified
|
||||||
: Icons.hourglass_bottom,
|
: Icons.hourglass_bottom,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: isVerified
|
color: widget.isVerified
|
||||||
? AppColors.success
|
? AppColors.success
|
||||||
: AppColors.textHint,
|
: AppColors.textHint,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
isVerified ? 'Verified' : 'Pending',
|
widget.isVerified ? 'Verified' : 'Pending',
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
color: isVerified
|
color: widget.isVerified
|
||||||
? AppColors.success
|
? AppColors.success
|
||||||
: AppColors.textHint,
|
: AppColors.textHint,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|
@ -174,10 +202,10 @@ class CarCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (name != null && name!.isNotEmpty) ...[
|
if (widget.name != null && widget.name!.isNotEmpty) ...[
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
name!,
|
widget.name!,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: theme.textTheme.titleSmall?.copyWith(
|
style: theme.textTheme.titleSmall?.copyWith(
|
||||||
|
|
@ -185,10 +213,10 @@ class CarCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
if (series != null && series!.isNotEmpty) ...[
|
if (widget.series != null && widget.series!.isNotEmpty) ...[
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
series!,
|
widget.series!,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
|
|
@ -196,7 +224,7 @@ class CarCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
if (year != null) ...[
|
if (widget.year != null) ...[
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -204,7 +232,7 @@ class CarCard extends StatelessWidget {
|
||||||
size: 12, color: AppColors.textHint),
|
size: 12, color: AppColors.textHint),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
'$year',
|
'${widget.year}',
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
color: AppColors.textHint,
|
color: AppColors.textHint,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue