I'm using platform channels to integrate a payment gateway SDK in my flutter application. I implemented the payment SDK code in my Flutter application's MainActivity.kt and call that code via platform channel which launches the checkout page and completes the payment process.
The problem is that when there is code in the MainActivity.kt the image picker stops working. The picker window is opened but clicking on any image does nothing. When I comment out the code in the MainActivity.kt the image picker starts to work as expected.
Another weird behavior that I have observed is that when the application is cold launched the plugin opens the picker window(However the image is still not selected), closing and reopening the picker again throws the following exception
[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: PlatformException(already_active, Image picker is already active, null)
The payment gateway SDK has the following dependencies which I have added in the app level gradle file(All of these are latest and upgraded as per need of androidx migrations)
implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.browser:browser:1.2.0'
implementation 'com.google.android.gms:play-services-wallet:18.1.1'
implementation 'io.card:android-sdk:5.5.1'
Version details are as follows
flutter: 1.20.4(Stable)
image_picker: ^0.6.7+11
module:
androidX: true
Here is my MainActivity.kt
import android.content.Intent
import com.oppwa.mobile.connect.checkout.dialog.CheckoutActivity
import com.oppwa.mobile.connect.checkout.meta.CheckoutSettings
import com.oppwa.mobile.connect.exception.PaymentError
import com.oppwa.mobile.connect.provider.Connect
import com.oppwa.mobile.connect.provider.Transaction
import com.oppwa.mobile.connect.provider.TransactionType
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
internal var resourcePath: String? = null
internal var mResult: MethodChannel.Result? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
if (call.method == "hyperpay") {
mResult = result
openCheckoutUI(call.argument<String>("checkoutID"))
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
if (resourcePath != null && hasCallbackScheme(intent)) {
mResult?.success("200")
}
}
private fun hasCallbackScheme(intent: Intent): Boolean {
val scheme = intent.scheme
return "checkoutui" == scheme
}
private fun openCheckoutUI(checkoutId: String?) {
val checkoutSettings = createCheckoutSettings(checkoutId, "checkoutui")
/* Set up the Intent and start the checkout activity. */
val intent = checkoutSettings.createCheckoutActivityIntent(this)
startActivityForResult(intent, CheckoutActivity.REQUEST_CODE_CHECKOUT)
}
private fun createCheckoutSettings(checkoutId: String?, callbackScheme: String): CheckoutSettings {
return CheckoutSettings(checkoutId!!, Constants.Config.PAYMENT_BRANDS,
Connect.ProviderMode.TEST)
.setShopperResultUrl("$callbackScheme://callback")
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
/* Override onActivityResult to get notified when the checkout process is done. */
if (requestCode == CheckoutActivity.REQUEST_CODE_CHECKOUT) {
when (resultCode) {
CheckoutActivity.RESULT_OK -> {
/* Transaction completed. */
val transaction = data.getParcelableExtra<Transaction>(
CheckoutActivity.CHECKOUT_RESULT_TRANSACTION)
resourcePath = data.getStringExtra(
CheckoutActivity.CHECKOUT_RESULT_RESOURCE_PATH)
/* Check the transaction type. */
if (transaction.transactionType == TransactionType.SYNC) {
mResult?.success("201")
}
}
CheckoutActivity.RESULT_CANCELED -> {
mResult?.success("403")
}
CheckoutActivity.RESULT_ERROR -> {
val error = data.getParcelableExtra<PaymentError>(
CheckoutActivity.CHECKOUT_RESULT_ERROR)
mResult?.error("400", error.errorMessage,error.errorInfo)
}
}
}
}
companion object {
private val CHANNEL = "com.abc.xyz/hyperpay"
}
}
Here is my Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.abc.xyz>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Permissions options for the `storage` group -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Permissions options for the `camera` group -->
<uses-permission android:name="android.permission.CAMERA"/>
<application
android:name="io.flutter.app.FlutterApplication"
android:label="App Name"
android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher">
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<activity
android:name="com.oppwa.mobile.connect.checkout.dialog.CheckoutActivity"
android:windowSoftInputMode="adjustResize"
android:exported="false"
android:launchMode="singleTop"/>
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<!-- OPPWA Payment Gateway Integration -->
<data android:scheme="checkoutui"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
<!-- Firebase Cloud Messaging/Notifications -->
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
Flutter doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 1.20.4, on Mac OS X 10.15.7 19H2, locale en-US)
[!] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
! Some Android licenses not accepted. To resolve this, run: flutter doctor
--android-licenses
[✓] Xcode - develop for iOS and macOS (Xcode 12.0.1)
[!] Android Studio (version 3.5)
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
[✓] VS Code (version 1.49.3)
[✓] Connected device (1 available)
! Doctor found issues in 2 categories.
I'm using platform channels to integrate a payment gateway SDK in my flutter application. I implemented the payment SDK code in my Flutter application's MainActivity.kt and call that code via platform channel which launches the checkout page and completes the payment process.
The problem is that when there is code in the MainActivity.kt the image picker stops working. The picker window is opened but clicking on any image does nothing. When I comment out the code in the MainActivity.kt the image picker starts to work as expected.
Another weird behavior that I have observed is that when the application is cold launched the plugin opens the picker window(However the image is still not selected), closing and reopening the picker again throws the following exception
The payment gateway SDK has the following dependencies which I have added in the app level gradle file(All of these are latest and upgraded as per need of androidx migrations)
Version details are as follows
Here is my MainActivity.kt
Here is my Manifest
Flutter doctor output