> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sudo.africa/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter SDK

> Sudo Cloud Card Flutter SDK.

## Package

* SDK: `cloudcard_flutter`
* Version: **0.1.1** ([pub.dev/packages/cloudcard\_flutter](https://pub.dev/packages/cloudcard_flutter))

<Info>
  For terminal transaction processing, Flutter has a companion package: <a href="https://pub.dev/packages/flutter_tappa">flutter\_tappa</a>.
</Info>

## Installation

Add the dependency:

```yaml theme={null}
# pubspec.yaml
dependencies:
  cloudcard_flutter: ^0.1.1
```

Then run:

```bash theme={null}
flutter pub get
```

## Android Repository Setup

For Gradle \< 7 (`android/build.gradle`):

```groovy theme={null}
allprojects {
  repositories {
    google()
    mavenCentral()
    maven {
      url = uri("https://sdk.sudo.africa/repository/maven-releases/")
      credentials {
        username = project.findProperty("maven.repo.username") ?: ""
        password = project.findProperty("maven.repo.password") ?: ""
      }
    }
  }
}
```

For Gradle 7+ (`android/settings.gradle`):

```groovy theme={null}
dependencyResolutionManagement {
  repositories {
    google()
    mavenCentral()
    maven {
      url = uri("https://sdk.sudo.africa/repository/maven-releases/")
      credentials {
        username = providers.gradleProperty("maven.repo.username").orNull ?: System.getenv("NEXUS_USER")
        password = providers.gradleProperty("maven.repo.password").orNull ?: System.getenv("NEXUS_PASS")
      }
    }
  }
}
```

Set credentials in `gradle.properties`:

```properties theme={null}
maven.repo.username=your_sudo_username
maven.repo.password=your_sudo_password
```

Or set environment variables:

```bash theme={null}
export NEXUS_USER=your_sudo_username
export NEXUS_PASS=your_sudo_password
```

## Android Manifest Configuration

Register the HCE service and receiver inside `<application>` in `android/app/src/main/AndroidManifest.xml`.

```xml theme={null}
<service
    android:name="com.sudo.cloud_card.HCEService"
    android:enableOnBackInvokedCallback="true"
    android:enabled="true"
    android:exported="true"
    android:permission="android.permission.BIND_NFC_SERVICE">
    <intent-filter>
        <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

<receiver
    android:name="africa.sudo.cloudcard_flutter.HceEventReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="${packageName}.CLOUDCARD_EVENT" />
    </intent-filter>
</receiver>
```

## iOS Setup

Create `.env` in your project root:

```bash theme={null}
SUDO_USER=your_sudo_username
SUDO_PASS=your_sudo_password
```

Run:

```bash theme={null}
export $(grep -v '^#' .env | xargs)
TMPHOME=$(mktemp -d)
trap 'rm -rf "$TMPHOME"' EXIT
export HOME="$TMPHOME"
cat > "$HOME/.netrc" <<EOF_NETRC
machine sdk.sudo.africa
login $SUDO_USER
password $SUDO_PASS
EOF_NETRC
chmod 600 "$HOME/.netrc"
cd ios && pod install
```

## Initialization

Initialize once near app startup.

```dart theme={null}
import 'package:cloudcard_flutter/cloudcard_flutter.dart';

final cloudCard = CloudCardFlutter();

Future<void> initializeCloudCard() async {
  await cloudCard.init(
    isSandBox: true,
    onCardScanned: (CloudCardEvent event) {
      print('scan started: ${event.eventType} ${event.message}');
    },
    onScanComplete: (CloudCardEvent event) {
      print('scan done: ${event.isSuccess} amount=${event.amount}');
    },
  );
}
```

Environment:

* `isSandBox: true` for sandbox
* `isSandBox: false` for production

## Health Checks

```dart theme={null}
final isSupported = await cloudCard.isDeviceSupported();
final isNfcEnabled = await cloudCard.isNfcEnabled();
final isDefaultApp = await cloudCard.isDefaultPaymentApp();

if (isSupported && isNfcEnabled == true && isDefaultApp == false) {
  await cloudCard.launchDefaultPaymentAppSettings();
}
```

## Optional Security Behavior

```dart theme={null}
await cloudCard.setRequireAuth(true);
```

Use `setRequireAuth(true)` if you want foreground access and biometric checks during payment access.

## Register Card

You register cards with `RegistrationData`.

Required fields:

* `walletId`
* `paymentAppInstanceId`
* `accountId`
* `jwtToken`

Optional fields:

* `secret`
* `cardNumber`
* `expiryDate`
* `cardHolderName`

```dart theme={null}
final cloudCard = CloudCardFlutter();

final registrationData = RegistrationData(
  walletId: 'institution-id',
  paymentAppInstanceId: 'device-instance-id',
  accountId: 'card-or-account-id',
  jwtToken: 'onboarding-jwt-token',
  secret: 'optional-secret',
  cardNumber: '4111111111111111',
  expiryDate: '12/25',
  cardHolderName: 'John Doe',
);

final result = await cloudCard.registerCard(registrationData);
if (result.status == Status.SUCCESS) {
  print('Card registered');
} else {
  print('Registration failed: ${result.message}');
}
```

## Digitalization Fetch Pattern

If you already have your own card-management system, you can build and pass your own `RegistrationData` directly to `registerCard` using the required fields `walletId`, `paymentAppInstanceId`, `accountId`, and `jwtToken`.

If you do not manage that payload yourself, fetch the onboarding or digitalization payload from your backend first, then pass it to `registerCard`. For endpoint details, see [Card Digitalization Reference](/reference/card-digitalization).

```dart theme={null}
final digitalizeUrl = Uri.parse(
  'https://api.sandbox.sudo.cards/cards/digitalize/{id}',
);

final res = await http.get(
  digitalizeUrl,
  headers: {
    'Accept': 'text/plain',
    'Content-Type': 'application/json',
    'platform': 'android',
    'Authorization': accessToken,
  },
);
```

## Card Management

### Get Cards

```dart theme={null}
final result = await CloudCardFlutter().getCards();
if (result.status == Status.SUCCESS && result.data is List<CardData>) {
  final cards = result.data as List<CardData>;
  for (final card in cards) {
    print('${card.id} ${card.maskedPan} active=${card.isActive}');
  }
}
```

### Freeze or Unfreeze

```dart theme={null}
await CloudCardFlutter().freezeUnfreezeCard(
  cardId: cardId,
  isFreeze: true,
);
```

Set `isFreeze: false` to unfreeze.

### Delete Card

```dart theme={null}
await CloudCardFlutter().deleteCard(cardId);
```

### Wipe Wallet

```dart theme={null}
await CloudCardFlutter().wipeWallet();
```

Use this only after explicit user confirmation.

### Manual Key Replenishment

```dart theme={null}
final result = await CloudCardFlutter().manualKeyReplenishment();
```

### Token Usage Summary

```dart theme={null}
final result = await CloudCardFlutter().tokenSummary();
if (result.status == Status.SUCCESS && result.data != null) {
  final summary = TokensUsageSummary.fromMap(result.data);
  print('total=${summary.totalTokens} balance=${summary.tokensBalance}');
}
```

## QR and Transactions

### Generate EMV QR

```dart theme={null}
final result = await CloudCardFlutter().getEmvQr(
  cardId: cardId,
  amount: '000000000200',
  foregroundColorHex: '#000000',
  backgroundColorHex: '#FFFFFF',
);

if (result.status == Status.SUCCESS && result.data != null) {
  showDialog(
    context: context,
    builder: (_) => AlertDialog(content: Image.memory(result.data)),
  );
}
```

`amount` is in the smallest unit and typically zero-padded.

### Read Saved Transactions

`getSavedTransactions()` returns recent cached transactions, up to 5.

```dart theme={null}
final result = await CloudCardFlutter().getSavedTransactions();
if (result.status == Status.SUCCESS && result.data is List<SavedTransaction>) {
  final txs = result.data as List<SavedTransaction>;
  for (final tx in txs) {
    print('${tx.amount} ${tx.currency} ${tx.timestamp} ${tx.type}');
  }
}
```

## NFC and Settings

```dart theme={null}
final cloudCard = CloudCardFlutter();

final isNfcEnabled = await cloudCard.isNfcEnabled();
final isDefaultPaymentApp = await cloudCard.isDefaultPaymentApp();

if (isNfcEnabled == true && isDefaultPaymentApp == false) {
  await cloudCard.launchDefaultPaymentAppSettings();
}
```

### NFC Chip Indicator Helpers

```dart theme={null}
await cloudCard.showNfcChipIndicator(
  duration: const Duration(seconds: 5),
  colorHex: '#AF5298',
);

final position = await cloudCard.getNfcChipPosition();
print('${position?.x} ${position?.y} ${position?.confidence}');

await cloudCard.hideNfcChipIndicator();
```

## Requirements

* Flutter 2.5.0+
* Android API 21+
* iOS 13.0+

NFC-enabled device is required for NFC payments, but not for QR.

## Troubleshooting

### Initialization Fails

* Confirm SDK repository credentials are set correctly on Android.
* Confirm the iOS `.netrc` auth step was completed before `pod install`.
* Check that you called `init()` before card operations.

### NFC Not Working

* Confirm the device supports NFC.
* Confirm NFC is enabled in system settings.
* Confirm your app is set as default payment app when required.
* Verify the HCE service and receiver entries in `AndroidManifest.xml`.
* Confirm the receiver class is `africa.sudo.cloudcard_flutter.HceEventReceiver`.

### Card Registration Fails

* Verify `walletId`, `paymentAppInstanceId`, `accountId`, and `jwtToken`.
* Ensure the onboarding token is still valid.
* Check that your backend is issuing tokens for the correct environment.

### QR Generation Fails

* Use a valid `cardId`.
* Ensure `amount` is formatted correctly.

### Empty Transaction List

* `getSavedTransactions()` uses local cache and returns recent transactions only.
* Complete at least one transaction flow first, then reload.

## Support

Contact [support@sudo.africa](mailto:support@sudo.africa).
