Flutter integration

Bridge the SDK through a MethodChannel: native module, Dart wrapper, and usage.

Step 1: Add dependency

In android/app/build.gradle:

dependencies {
    implementation "io.github.blinc-technologies:blinc-pos-sdk-ui:0.1.2"
    implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.22"
}

Step 2: Implement the native MethodChannel

MainActivity must extend FlutterFragmentActivity

This is required to support AndroidX components.

package com.your.package

import android.os.Bundle
import com.blinc.blincpos.BlincPosSdk
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

class MainActivity : FlutterFragmentActivity() {

    private val CHANNEL              = "com.blinc.pos/sdk"
    private val scope                = CoroutineScope(Dispatchers.Main)
    private var pendingResult: MethodChannel.Result? = null
    private var channel: MethodChannel? = null
    private val terminalId           = "T0001"
    private val merchantId           = "00000001"
    private val paymentFacilitatorId = 1L

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        BlincPosSdk.registerLauncher(this) { result ->
            val response = mapOf(
                "status"        to result.status.name,
                "transactionId" to result.transactionId,
                "amount"        to result.amount,
                "currency"      to result.currency,
                "message"       to result.message
            )
            pendingResult?.success(response)
            pendingResult = null
        }
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        channel!!.setMethodCallHandler { call, result ->
            when (call.method) {
                "startTransaction" -> {
                    pendingResult = result
                    val amount   = call.argument<Number>("amount")?.toLong()
                    val metadata = call.argument<String>("metadata") ?: "{}"
                    val method   = call.argument<String>("method") ?: "NFC"

                    scope.launch {
                        val registered = BlincPosSdk.ensureRegistered(
                            context              = this@MainActivity,
                            terminalId           = terminalId,
                            merchantId           = merchantId,
                            paymentFacilitatorID = paymentFacilitatorId,
                            environment          = Environment.TEST // Environment.LIVE for prod
                            challengeProvider    = { fetchChallengeFromDart() }
                        )
                        if (!registered) {
                            result.error("RegistrationError", "Could not register terminal", null)
                            pendingResult = null
                            return@launch
                        }
                        if (method == "QR") {
                            BlincPosSdk.startTransactionWithQR(
                                context = this@MainActivity, amount = amount, metadata = metadata
                            )
                        } else {
                            BlincPosSdk.startTransactionWithNFC(
                                context = this@MainActivity, amount = amount, metadata = metadata
                            )
                        }
                    }
                }
                else -> result.notImplemented()
            }
        }
    }

    private suspend fun fetchChallengeFromDart(): String? = withContext(Dispatchers.Main) {
        suspendCoroutine { cont ->
            channel?.invokeMethod("fetchChallenge", null, object : MethodChannel.Result {
                override fun success(value: Any?) { cont.resume(value as? String) }
                override fun error(code: String, msg: String?, details: Any?) { cont.resume(null) }
                override fun notImplemented() { cont.resume(null) }
            }) ?: cont.resume(null)
        }
    }
}

Step 3: Dart wrapper

import 'package:flutter/services.dart';

class BlincPosSdk {
  static const MethodChannel _channel = MethodChannel('com.blinc.pos/sdk');

  static void setup({required Future<String?> Function() fetchChallenge}) {
    _channel.setMethodCallHandler((call) async {
      if (call.method == 'fetchChallenge') {
        return await fetchChallenge();
      }
      return null;
    });
  }

  static Future<Map<String, dynamic>?> startTransaction({
    int? amount,
    String metadata = '{}',
    String method   = 'NFC',
  }) async {
    try {
      final Map<dynamic, dynamic>? result = await _channel.invokeMethod(
        'startTransaction',
        {'amount': amount, 'metadata': metadata, 'method': method},
      );
      return result?.cast<String, dynamic>();
    } catch (e) {
      print('Blinc SDK Error: $e');
      return null;
    }
  }
}

Usage

void main() {
  BlincPosSdk.setup(
    fetchChallenge: () => YourBackendApiClient.fetchChallenge(),
  );
  runApp(const MyApp());
}

void handlePayment() async {
  final result = await BlincPosSdk.startTransaction(
    amount:   199900,
    method:   'NFC',
    metadata: '{"orderId":"ORD-42"}',
  );

  if (result != null) {
    if (result['status'] == 'SUCCESS') {
      print('Paid! ID: ${result['transactionId']}');
    } else {
      print('Failed: ${result['message']}');
    }
  }
}

Did this page help you?