Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Changes:
* Fix: imageURL download time improvement

## 2.2.8
* Fix recommended size is shown in InPage even when there is no available size in the comparison screen
* Fix: recommended size is shown in InPage even when there is no available size in the comparison screen
* Fix: Virtusize button state when loading a new product
* Fix: Android: InPage Standard sometimes does not appear
* Fix: Android: VS widget sometimes does not show when navigating between different colors
Expand Down
62 changes: 58 additions & 4 deletions lib/src/utils/virtusize_product_image_loader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,65 @@ import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:virtusize_flutter_sdk/src/models/virtusize_server_product.dart';

const _imageLoadTimeout = Duration(seconds: 10);

Future<void> downloadProductImage(VirtusizeServerProduct product) async {
var image = await _downloadImage(product.imageURL ?? '');
image ??= await _downloadImage(product.cloudinaryImageURL ?? '');
final imageURL = product.imageURL;
final cloudinaryImageURL = product.cloudinaryImageURL;

// Build list of valid URLs to try in parallel
final futures = <Future<Image?>>[];
if (imageURL != null && imageURL.isNotEmpty) {
futures.add(_downloadImageWithTimeout(imageURL));
}
if (cloudinaryImageURL != null && cloudinaryImageURL.isNotEmpty) {
futures.add(_downloadImageWithTimeout(cloudinaryImageURL));
}

if (futures.isEmpty) return;

// Try all URLs in parallel, use the first successful result
final image = await _firstSuccessful(futures);

if (image != null) {
product.networkProductImage = image;
}
}

/// Returns the first successful (non-null) result from the futures.
/// If all futures complete with null, returns null.
Future<Image?> _firstSuccessful(List<Future<Image?>> futures) async {
if (futures.isEmpty) return null;

final completer = Completer<Image?>();
var pendingCount = futures.length;
var completed = false;

for (final future in futures) {
future.then((image) {
if (completed) return;
if (image != null) {
completed = true;
completer.complete(image);
} else {
pendingCount--;
if (pendingCount == 0 && !completed) {
completed = true;
completer.complete(null);
}
}
});
}

return completer.future;
}

Future<Image?> _downloadImageWithTimeout(String imageUrl) {
return _downloadImage(
imageUrl,
).timeout(_imageLoadTimeout, onTimeout: () => null);
}

Future<Image?> _downloadImage(String imageUrl) {
final Image networkImage = Image.network(imageUrl);
final Completer<Image?> completer = Completer<Image?>();
Expand All @@ -21,10 +71,14 @@ Future<Image?> _downloadImage(String imageUrl) {
stream.addListener(
ImageStreamListener(
(ImageInfo image, bool synchronousCall) {
completer.complete(networkImage);
if (!completer.isCompleted) {
completer.complete(networkImage);
}
},
onError: (Object exception, StackTrace? stackTrace) {
completer.complete(null);
if (!completer.isCompleted) {
completer.complete(null);
}
},
),
);
Expand Down
Loading