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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 2.2.8
* 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
* Update native SDK support to Android 2.12.15 & iOS 2.12.22

## 2.2.7
* Fix: the human icon thumbnail is missing in inpage
* Fix: opening the privacy policy redirects to an external browser
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ buildscript {
ext.cardview_version = "1.0.0"
ext.coroutines_version = "1.5.0"
ext.gradle_version = "8.7.0"
ext.virtusize_version = "2.12.13"
ext.virtusize_version = "2.12.15"

repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ class VirtusizeFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
private var virtusizeFlutterPresenter: VirtusizeFlutterPresenter =
object : VirtusizeFlutterPresenter {
override fun onValidProductCheck(productWithPCDData: VirtusizeProduct) {
channel.invokeMethod(
VirtusizeFlutterMethod.ON_PRODUCT_DATA_CHECK,
mutableMapOf(
VirtusizeFlutterKey.EXTERNAL_PRODUCT_ID to productWithPCDData.externalId,
VirtusizeFlutterKey.IS_VALID_PRODUCT to (productWithPCDData.productCheckData?.data?.validProduct ?: false),
// Post to main thread with delay to ensure Flutter widgets have time to subscribe
// This handles the case where cached data causes synchronous callback execution
scope.launch {
kotlinx.coroutines.delay(100)
channel.invokeMethod(
VirtusizeFlutterMethod.ON_PRODUCT_DATA_CHECK,
mutableMapOf(
VirtusizeFlutterKey.EXTERNAL_PRODUCT_ID to productWithPCDData.externalId,
VirtusizeFlutterKey.IS_VALID_PRODUCT to (productWithPCDData.productCheckData?.data?.validProduct ?: false),
)
)
)
}
}

override fun hasInPageError(
Expand All @@ -54,18 +59,27 @@ class VirtusizeFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
storeProduct: Product?,
bestUserProduct: Product?,
recommendationText: String?,
willFit: Boolean?,
) {
sendOnProductResult(storeProduct, "store")
sendOnProductResult(bestUserProduct, "user")
scope.launch {
kotlinx.coroutines.delay(100)
sendOnProductResult(storeProduct, "store")
sendOnProductResult(bestUserProduct, "user")

channel.invokeMethod(
VirtusizeFlutterMethod.ON_REC_CHANGE,
mutableMapOf(
VirtusizeFlutterKey.EXTERNAL_PRODUCT_ID to externalProductId,
VirtusizeFlutterKey.REC_TEXT to recommendationText,
VirtusizeFlutterKey.SHOW_USER_PRODUCT_IMAGE to true
// Show user product image only if:
// - willFit is not explicitly false (when false, it means "Your size not found")
// - AND there is a bestUserProduct available
val showUserProductImage = willFit != false && bestUserProduct != null

channel.invokeMethod(
VirtusizeFlutterMethod.ON_REC_CHANGE,
mutableMapOf(
VirtusizeFlutterKey.EXTERNAL_PRODUCT_ID to externalProductId,
VirtusizeFlutterKey.REC_TEXT to recommendationText,
VirtusizeFlutterKey.SHOW_USER_PRODUCT_IMAGE to showUserProductImage
)
)
)
}
}

override fun onLangugeClick(language: VirtusizeLanguage) {
Expand Down Expand Up @@ -210,6 +224,7 @@ class VirtusizeFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
return
}
virtusizeFlutter.setUserId(call.arguments.toString())
result.success(true)
}
VirtusizeFlutterMethod.LOAD_VIRTUSIZE -> {
val externalId = call.argument<String>(VirtusizeFlutterKey.EXTERNAL_PRODUCT_ID)
Expand All @@ -224,6 +239,7 @@ class VirtusizeFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
)

virtusizeFlutter.load(virtusizeProduct)
result.success(true)
}
VirtusizeFlutterMethod.OPEN_VIRTUSIZE_WEB_VIEW -> {
val externalProductId = call.arguments as? String
Expand All @@ -237,6 +253,7 @@ class VirtusizeFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
activity,
externalProductId
)
result.success(true)
}
VirtusizeFlutterMethod.GET_PRIVACY_POLICY_LINK -> {
result.success(
Expand Down
2 changes: 1 addition & 1 deletion example/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
2 changes: 1 addition & 1 deletion example/android/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pluginManagement {

plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.0" apply false
id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
}

Expand Down
21 changes: 12 additions & 9 deletions example/ios/RunnerTests/RunnerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,14 @@ class RunnerTests: XCTestCase {
VirtusizeFlutterKey.imageURL: "https://example.com/image.jpg"
]
)

var resultData: Any?
plugin.handle(call) { result in
resultData = result
}

XCTAssertNil(resultData)

XCTAssertNotNil(resultData)
XCTAssertEqual(resultData as? Bool, true)
}

func testLoadVirtusizeWithoutProductId() {
Expand All @@ -106,13 +107,14 @@ class RunnerTests: XCTestCase {
methodName: VirtusizeFlutterMethod.setUserId,
arguments: "test_user_id"
)

var resultData: Any?
plugin.handle(call) { result in
resultData = result
}

XCTAssertNil(resultData)

XCTAssertNotNil(resultData)
XCTAssertEqual(resultData as? Bool, true)
}

func testSetUserIdWithEmptyId() {
Expand All @@ -139,13 +141,14 @@ class RunnerTests: XCTestCase {
methodName: VirtusizeFlutterMethod.openVirtusizeWebView,
arguments: "product123"
)

var resultData: Any?
plugin.handle(call) { result in
resultData = result
}

XCTAssertNil(resultData)

XCTAssertNotNil(resultData)
XCTAssertEqual(resultData as? Bool, true)
}

func testOpenVirtusizeWebViewWithoutProductId() {
Expand Down
2 changes: 0 additions & 2 deletions example/lib/screens/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ class _HomeScreenState extends State<HomeScreen> {
VirtusizeInPageMini(
product: _product,
backgroundColor: Colors.blue,
horizontalMargin: 32,
),
Container(height: 16),

Expand All @@ -136,7 +135,6 @@ class _HomeScreenState extends State<HomeScreen> {
VirtusizeInPageStandard(
product: _product,
buttonBackgroundColor: Colors.amber,
horizontalMargin: 32,
),
Container(height: 16),

Expand Down
20 changes: 14 additions & 6 deletions ios/Classes/SwiftVirtusizeFlutterPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,20 @@ public class SwiftVirtusizeFlutterPlugin: NSObject, FlutterPlugin {
if let imageURLString = arguments[VirtusizeFlutterKey.imageURL] as? String {
imageURL = URL(string: imageURLString)
}

let product = VirtusizeProduct(externalId: externalProductId, imageURL: imageURL)
VirtusizeFlutter.load(product: product)
result(true)
case VirtusizeFlutterMethod.openVirtusizeWebView:
guard let externalProductId = call.arguments as? String else {
result(FlutterError.noArguments)
return
}

VirtusizeFlutter.openVirtusizeWebView(
externalId: externalProductId,
messageHandler: self)
result(true)
case VirtusizeFlutterMethod.getPrivacyPolicyLink:
result(VirtusizeFlutter.getPrivacyPolicyLink())
case VirtusizeFlutterMethod.sendOrder:
Expand Down Expand Up @@ -180,7 +182,8 @@ extension SwiftVirtusizeFlutterPlugin: VirtusizeFlutterProductEventHandler {
clientProductImageURL: String?,
storeProduct: VirtusizeServerProduct,
bestUserProduct: VirtusizeServerProduct?,
recommendationText: String) {
recommendationText: String,
willFit: Bool?) {
DispatchQueue.main.async { [self] in
self.flutterChannel?.invokeMethod(
VirtusizeFlutterMethod.onProduct,
Expand All @@ -193,7 +196,7 @@ extension SwiftVirtusizeFlutterPlugin: VirtusizeFlutterProductEventHandler {
VirtusizeFlutterKey.productStyle: storeProduct.productStyle
]
)

self.flutterChannel?.invokeMethod(
VirtusizeFlutterMethod.onProduct,
arguments: [
Expand All @@ -204,13 +207,18 @@ extension SwiftVirtusizeFlutterPlugin: VirtusizeFlutterProductEventHandler {
VirtusizeFlutterKey.productStyle: bestUserProduct?.productStyle
]
)


// Show user product image only if:
// - willFit is not explicitly false (when false, it means "Your size not found")
// - AND there is a bestUserProduct available
let showUserProductImage = willFit != false && bestUserProduct != nil

self.flutterChannel?.invokeMethod(
VirtusizeFlutterMethod.onRecChange,
arguments: [
VirtusizeFlutterKey.externalProductId: externalId,
VirtusizeFlutterKey.recText: recommendationText,
VirtusizeFlutterKey.showUserProductImage: true
VirtusizeFlutterKey.showUserProductImage: showUserProductImage
]
)
}
Expand Down
4 changes: 2 additions & 2 deletions ios/virtusize_flutter_sdk.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'virtusize_flutter_sdk'
s.version = '2.2.7'
s.version = '2.2.8'
s.summary = 'Virtusize SDK for Flutter.'
s.description = <<-DESC
This SDK helps clients to integrate Virtusize’s size and fit service into their Flutter applications for Android & iOS.
Expand All @@ -17,7 +17,7 @@ This SDK helps clients to integrate Virtusize’s size and fit service into thei
s.resources = 'Resources/**/*.json'
s.resource_bundle = { 'virtusize_flutter_sdk' => ['Resources/**/*.json'] }
s.dependency 'Flutter'
s.dependency 'Virtusize', '~> 2.12.19'
s.dependency 'Virtusize', '~> 2.12.22'
s.static_framework = true

s.platform = :ios, '14.0'
Expand Down
49 changes: 40 additions & 9 deletions lib/src/widgets/virtusize_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,15 @@ class VirtusizeButton extends StatefulWidget {
final Widget? child;
final VirtusizeStyle style;

const VirtusizeButton({
super.key,
required this.product,
required Widget this.child,
}) : style = VirtusizeStyle.none;
VirtusizeButton({required this.product, required Widget this.child})
: style = VirtusizeStyle.none,
super(key: ValueKey('button_${product.externalProductId}'));

const VirtusizeButton.vsStyle({
super.key,
VirtusizeButton.vsStyle({
required this.product,
this.style = VirtusizeStyle.black,
this.child,
});
}) : super(key: ValueKey('vs_button_${product.externalProductId}'));

@override
// ignore: library_private_types_in_public_api
Expand All @@ -38,6 +35,8 @@ class _VirtusizeButtonState extends State<VirtusizeButton> {

VSText _vsText = IVirtusizeSDK.instance.vsText;
bool _isValidProduct = false;
Timer? _productDataCheckTimeout;
bool _productDataCheckTimedOut = false;

@override
void initState() {
Expand All @@ -56,22 +55,54 @@ class _VirtusizeButtonState extends State<VirtusizeButton> {
productDataCheck.externalProductId) {
return;
}
_productDataCheckTimeout?.cancel();
setState(() {
_isValidProduct = productDataCheck.isValidProduct;
_productDataCheckTimedOut = false;
});
});

// Start timeout timer for product data check
_startProductDataCheckTimeout();
}

void _startProductDataCheckTimeout() {
_productDataCheckTimeout?.cancel();
_productDataCheckTimedOut = false;

_productDataCheckTimeout = Timer(Duration(seconds: 10), () {
if (!mounted) return;
if (!_isValidProduct) {
setState(() {
_productDataCheckTimedOut = true;
});
}
});
}

@override
void didUpdateWidget(VirtusizeButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.product.externalProductId != widget.product.externalProductId) {
setState(() {
_isValidProduct = false;
});
_startProductDataCheckTimeout();
}
}

@override
void dispose() {
_vsTextSubscription.cancel();
_pdcSubscription.cancel();
_productDataCheckTimeout?.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
if (_isValidProduct == true) {
// Show the button only when the product is confirmed valid
if (_isValidProduct) {
switch (widget.style) {
case VirtusizeStyle.none:
return widget.child!;
Expand Down
Loading
Loading