[iOS] AdaptyUI Close button not working (flutter_background_service conflict)
You guys had this issue for android real devices, and now have for ios too with last adapty_flutter: ^3.3.0. Why this main issue still occures? I was waiting u guys fix that issue for android but now ios paywalls not closing too. Please can u handle that bug???
There is no error in terminal, Paywalls close button not working.
bool isAdaptyActivated = await Adapty().isActivated();
if (!isAdaptyActivated) {
const mediaCache = AdaptyUIMediaCacheConfiguration(
memoryStorageTotalCostLimit: 100 * 1024 * 1024, // 100MB
memoryStorageCountLimit: 2147483647, // 2^31 - 1, max int value in Dart
diskStorageSizeLimit: 100 * 1024 * 1024, // 100MB
);
await Adapty().activate(
configuration: AdaptyConfiguration(apiKey: adaptyApiKey)
..withMediaCacheConfiguration(mediaCache)
..withActivateUI(true)
);
##############################
void paywallViewDidPerformAction(AdaptyUIView view, AdaptyUIAction action) {
switch (action) {
case const CloseAction():
view.dismiss();
AnalyticsComponent.simpleEvent(
event: "${view.placementId}_purchase_dismissed");
AnalyticsComponent.logCumulativeEvent(event: "purchase_dismissed");
break;
case const AndroidSystemBackAction():
view.dismiss();
AnalyticsComponent.simpleEvent(
event: "${view.placementId}_purchase_dismissed");
AnalyticsComponent.logCumulativeEvent(event: "purchase_dismissed");
break;
case OpenUrlAction(url: final url):
debugPrint("Opening URL: ${action.url}");
final Uri uri = Uri.parse(url);
launchUrl(uri, mode: LaunchMode.inAppBrowserView);
break;
default:
break;
}
}
void main() async {
runZonedGuarded(() async {
await MainInitializers.runAllInitializers();
runApp(
const MainWidget(),
);
}, (Object error, StackTrace stackTrace) async {
});
}
class MainWidget extends StatefulWidget {
const MainWidget({
super.key,
});
@override
State<MainWidget> createState() => _MainWidgetState();
}
class _MainWidgetState extends State<MainWidget>
with WidgetsBindingObserver
implements AdaptyUIObserver {
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
debugPrint(state.toString());
}
if (state == AppLifecycleState.resumed) {
debugPrint(state.toString());
}
}
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
useInheritedMediaQuery: true, //do not change it's for keyboard open state
builder: (context, child) {
return getx.GetMaterialApp(
debugShowCheckedModeBanner: false,
getPages: AppPages.pages,
navigatorObservers: [
FirebaseAnalyticsObserver(
analytics: FirebaseAnalytics.instance,
routeFilter: (_) => true,
//sends the exact route name to firebase
nameExtractor: (settings) {
//remove slash from getX route names and then send them to analytics
return (settings.name ?? "").replaceAll("/", "");
},
),
],
initialRoute: AppRoutes.splash,
initialBinding: GlobalBinding(),
builder: (context, child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
boldText: false,
textScaler: const TextScaler.linear(1.0),
),
child: child!,
);
},
theme: AppTheme.lightTheme(),
themeMode: ThemeMode.dark,
//darkTheme: AppTheme.darkTheme(),
//translations: AppTranslations(),
defaultTransition: getx.Transition.fadeIn,
locale: getx.Get.deviceLocale,
supportedLocales: L10n.all,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
);
},
);
}
@override
void paywallViewDidFailPurchase(
AdaptyUIView view, AdaptyPaywallProduct product, AdaptyError error) {
AnalyticsComponent.simpleEvent(
event: "${view.placementId}_paywall_fail_purchase");
AnalyticsComponent.logCumulativeEvent(event: "paywall_fail_purchase");
}
@override
void paywallViewDidFinishPurchase(AdaptyUIView view,
AdaptyPaywallProduct product, AdaptyPurchaseResult result) async {
if (result is AdaptyPurchaseResultSuccess) {
debugPrint("PAYMENT FINISH WITH SUCCESS!");
await Get.find<PremiumController>()
.successPayment(goToMain: false, product: product);
view.dismiss();
AnalyticsComponent.simpleEvent(
event: "${view.placementId}_paywall_finish_purchase");
} else if (result is AdaptyPurchaseResultUserCancelled) {
debugPrint("PAYMENT CANCELLED!");
AnalyticsComponent.simpleEvent(
event: "${view.placementId}_paywall_cancel_purchase");
AnalyticsComponent.logCumulativeEvent(event: "paywall_cancel_purchase");
} else if (result is AdaptyPurchaseResultPending) {}
}
@override
void paywallViewDidSelectProduct(AdaptyUIView view, String productID) {
AnalyticsComponent.simpleEvent(
event: "${view.placementId}_paywall_select_$productID");
AnalyticsComponent.logCumulativeEvent(event: "paywall_select_product");
}
@override
void paywallViewDidStartPurchase(
AdaptyUIView view, AdaptyPaywallProduct product) {
AnalyticsComponent.simpleEvent(
event: "${view.placementId}_paywall_start_purchase");
AnalyticsComponent.logCumulativeEvent(event: "paywall_start_purchase");
}
@override
void paywallViewDidFinishRestore(AdaptyUIView view, AdaptyProfile profile) {}
@override
void paywallViewDidPerformAction(AdaptyUIView view, AdaptyUIAction action) {
AdaptyPaymentComponent().paywallViewDidPerformAction(view, action);
}
@override
void paywallViewDidFailRendering(AdaptyUIView view, AdaptyError error) {}
@override
void paywallViewDidFailRestore(AdaptyUIView view, AdaptyError error) {}
@override
void paywallViewDidFailLoadingProducts(
AdaptyUIView view, AdaptyError error) {}
@override
void paywallViewDidStartRestore(AdaptyUIView view) {}
}
Did you set AdaptyUIObserver?
AdaptyUI().setObserver(this);
Current Status:
- @Brechard reported an issue with closing the paywall on versions 3.2.3 and 3.1.0 when using an iPad Air (5th generation, 2022) running iOS 18.2. However, everything works as expected on iOS 16.7.10 with an older iPad (5th generation).
- @Brechard also confirmed that the issue does not occur when testing our Example app on both devices.
- @PBomermustekiin reported a similar issue with iOS devices when using version 3.3.0.
Observations:
Based on the above, we can deduce the following:
- The issue does not appear to have been introduced in 3.2.0 or 3.3.0, as it is also reproducible in version 3.1.0.
- The issue seems to vary across iOS versions, being reproducible on iOS 18.2 and the iPad Air (5th generation).
- The issue is not reproducible in our Example app.
At this point, it’s unclear whether @PBomermustekiin and @Brechard are encountering the same issue, as @PBomermustekiin has not tested with our Example app.
Next Steps:
To help us investigate further, please provide the following details (similar to the Android issue):
-
Paywall Information
- Provide the
paywallId(paywall.instanceIdentity) andpaywallVariationId(paywall.variationId). - This could help identify if the issue is related to the paywall configuration rather than the SDK. Testing your specific paywall setup will be key.
- Provide the
-
SDK Logs
- Collect detailed logs from both the Dart and Swift layers of the SDK.
- Running the app in Xcode to capture logs will be particularly helpful.
-
Minimal Reproducible Project
- If possible, share a minimal working project where the issue occurs.
- Including all dependencies will allow us to identify potential clashes.
-
Example App Testing
- @PBomermustekiin, please test our demo app to determine whether this is an integration issue specific to your code.
Let’s focus on these steps to narrow down the root cause and address the problem effectively.
I had the same issue. It was working fine in the demo app but the button stopped working when I added it to my own app. After a lot of trial and error. I realized that removing the Flutter background service fixed the issue and everything started working fine. I’m not sure exactly how it conflicts with the background service but if you’re using a similar service, it might be worth a try.
Hi @onuryagci53! It would be great if you could provide a demo project that demonstrates how this background service is preventing our SDK from working correctly. This would greatly help us in attempting to fix the compatibility issue.
Hi @x401om I test it on demo app now.
background_service.dart
import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
Future<void> initializeBackgroundService() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: true,
isForegroundMode: false,
),
iosConfiguration: IosConfiguration(
autoStart: true,
onForeground: onStart,
onBackground: onIosBackground,
),
);
}
@pragma('vm:entry-point')
Future<bool> onIosBackground(ServiceInstance service) async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.reload();
final log = preferences.getStringList('log') ?? <String>[];
log.add(DateTime.now().toIso8601String());
await preferences.setStringList('log', log);
return true;
}
@pragma('vm:entry-point')
void onStart(ServiceInstance service) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString("status", "Service Started");
if (service is AndroidServiceInstance) {
service.on('stopService').listen((event) {
service.stopSelf();
});
}
Timer.periodic(const Duration(seconds: 1), (timer) async {
if (service is AndroidServiceInstance) {
if (!(await service.isForegroundService())) {
print('Service is running in background: ${DateTime.now()}');
}
}
final deviceInfo = DeviceInfoPlugin();
String? device;
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
device = androidInfo.model;
} else if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
device = iosInfo.model;
}
service.invoke(
'update',
{
"current_date": DateTime.now().toIso8601String(),
"device": device,
},
);
});
}
This one is my service file. I did initialize the service in main method.(You may need to configure AndroidManifest)
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeBackgroundService();
runApp(MyApp());
}
And the close button stopped functioning.
flutter_background_service: ^5.1.0 this is the bg service version
Btw it giving this log
W/FlutterJNI(32063): Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: flutter.adapty.com/adapty. Response ID: 12
on debug console when I press the close button.
Update: In background service If I set the autostart to false paywall works properly. But after I start the service and stop it, it still does not work.
The issue should be fixed in 3.4.0