# Integrate the Genuin SDK with Flutter apps

Add the genuin_sdk plugin to a Flutter project and configure the Android and iOS hosts it needs.

The Genuin Flutter SDK is the [genuin_sdk](https://pub.dev/packages/genuin_sdk) plugin, version 1.0.1 on pub.dev. It wraps the native Genuin SDKs: `genuin_android_sdk` 1.1.9.3 on Android and the `GenuinSDK` 1.1.9.1 pod on iOS. The plugin gives you four embed widgets and a `GenuinSdk` class for sign-in, push notifications, and playback control.

This page gets the project to build: the dependency, the Android and iOS host configuration, and the permissions the native SDKs bring with them. When the project builds, continue with [Initialize the Genuin SDK and load a feed in Flutter](https://begenuin.com/developers/developer-resources/sdk/flutter-sdk-developer-reference-how-do-i-initialize-the-genuin-sdk-and-load-a-feed) to initialize the SDK and render your first embed.

## Requirements

| | Minimum |
| --- | --- |
| Flutter | 3.24.4. The plugin requires Dart ^3.5.4, and Flutter 3.24.4 is the first stable release that ships it. |
| Android | API 24 (Android 7.0), compileSdk 34, Android Gradle Plugin 8.1.0, Kotlin 1.8.22. The launcher activity must extend FlutterFragmentActivity. |
| iOS | 13.0, Swift 5, CocoaPods. The plugin's example app builds for iOS 15.0. |
| Native SDKs | Fixed by the plugin: org.bitbucket.genuindev:genuin_android_sdk:1.1.9.3 from JitPack, and the GenuinSDK CocoaPod at ~> 1.1.9.1. |
| Orientation | Portrait. The native Android screens lock to portrait. |
| Devices | Phones with a camera, a front camera, and a microphone. The Android SDK declares all three as required hardware. |

You also need an **API key** for your brand and at least one **embed ID**, both from Brand Control Center. The key is the same one the Android, iOS, and Web SDKs use.

> **Note** The plugin pins native SDK 1.1.9.x. Integrate the Genuin Android SDK and Integrate the Genuin iOS SDK describe version 2.1.5, which has features the Flutter plugin doesn't expose, such as placements, contextual parameters, and event callbacks.

## Step 1: Add the dependency

1. Add the package to `pubspec.yaml`:

```yaml title="pubspec.yaml"
dependencies:
  flutter:
    sdk: flutter
  genuin_sdk: ^1.0.1
```

2. Run `flutter pub get`.

The package has one library per widget. `package:genuin_sdk/genuin_sdk.dart` exports only the `GenuinSdk` class. The widgets come from `genuin_carousel_embed_view.dart`, `genuin_feed_embed_view.dart`, `genuin_standard_wall_view.dart`, and `genuin_feed_view.dart`. The [initialization page](https://begenuin.com/developers/developer-resources/sdk/flutter-sdk-developer-reference-how-do-i-initialize-the-genuin-sdk-and-load-a-feed) lists the import for each widget.

## Step 2: Configure the Android project

### Add the JitPack repository

The native Android SDK is published on JitPack, not Maven Central. In `android/build.gradle`, add JitPack for every project:

```groovy title="android/build.gradle"
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://jitpack.io" }
    }
}
```

If Gradle reports `Could not find org.bitbucket.genuindev:genuin_android_sdk:1.1.9.3`, this repository is missing.

### Set the minimum SDK version

The plugin declares `minSdk 24`, which is higher than the Flutter template default. In `android/app/build.gradle`:

```groovy title="android/app/build.gradle"
android {
    compileSdk = flutter.compileSdkVersion   // 34 or later

    defaultConfig {
        minSdk = 24
        targetSdk = flutter.targetSdkVersion
    }
}
```

Make sure `android/gradle.properties` contains `android.useAndroidX=true` and `android.enableJetifier=true`. The plugin's example app sets both.

### Update the manifest

Make three changes to the Flutter template. Replace the SDK's `android:label`, and give the activity `singleTask` launch mode so deep links reach the running activity. Remove `android:taskAffinity=""` if the template added it.

```xml title="android/app/src/main/AndroidManifest.xml"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:label="{{APP_NAME}}"
        android:icon="@mipmap/ic_launcher"
        tools:replace="android:label">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTask"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
```

`tools:replace="android:label"` is required because the SDK's own manifest sets `android:label`, and the manifest merger refuses two different values.

> **Tip** The plugin's README recommends android:windowSoftInputMode="adjustPan" for smoother SDK screens. The example app ships with adjustResize; both work.

### Register your white-labelled domain (Optional)

To open Genuin links in your app, add an `https` intent filter for your [white-labelled domain](https://begenuin.com/developers/browse-by-products/onsite/how-can-you-white-label-your-community-under-your-own-domain) inside the same `<activity>`:

```xml title="AndroidManifest.xml"
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:host="{{YOUR_DOMAIN}}" android:scheme="https" />
</intent-filter>
```

`autoVerify` needs a Digital Asset Links file on your domain. Genuin serves it from your brand's configuration once you send your package name and signing fingerprints; [Handle Genuin deep links in your app](https://begenuin.com/developers/developer-resources/sdk/handle-genuin-deep-links-in-your-app) covers the request. The code that hands the link to the SDK is on the [initialization page](https://begenuin.com/developers/developer-resources/sdk/flutter-sdk-developer-reference-how-do-i-initialize-the-genuin-sdk-and-load-a-feed).

### Use a Material theme

The native SDK is built on Material Components, and its views throw when the host theme isn't a Material Components theme. Change the two styles in `android/app/src/main/res/values/styles.xml`, and make the same change in `values-night/styles.xml`:

```xml title="android/app/src/main/res/values/styles.xml"
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="LaunchTheme" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
    <style name="NormalTheme" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">
        <item name="android:windowBackground">?android:colorBackground</item>
    </style>
</resources>
```

The `.Bridge` variants keep AppCompat attributes working, so the rest of your app doesn't need to change.

### Add ProGuard rules

The Android SDK ships no consumer rules: the `proguard.txt` inside its AAR is empty. Release builds shrink with R8, and without rules they strip classes the SDK reaches through reflection. Flutter adds `android/app/proguard-rules.pro` to release builds when the file exists, so create it with these rules:

```proguard title="proguard-rules.pro"
-keep class com.bumptech.glide.** { *; }
-keep public class * { public *; protected *; }
-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>
-keep class com.begenuin.begenuin.data.model.** { <fields>; }
-keep class com.begenuin.begenuin.vo.** { <fields>; }
-keep class com.giphy.sdk.core.models.** { *; }
-keep class com.giphy.sdk.ui.views.** { *; }
-keep class com.google.mediapipe.solutioncore.** { *; }
-keep class com.google.protobuf.** { *; }
-keep public class * implements java.lang.reflect.Type
-dontwarn com.begenuin.sdk.BR
```

These are the keep rules. The full file also carries about 100 `-dontwarn` lines for optional dependencies. Copy it from the plugin's example app, at `example/android/app/proguard-rules.pro` inside the `genuin_sdk-1.0.1` folder of your pub cache.

> **Caution** -keep public class \* { public \*; protected \*; } keeps every public class in your app, which disables most of R8's obfuscation. Narrow it to the SDK's packages only after testing a release build with the narrower rule.

### Replace the loading animation (Optional)

The SDK shows a Lottie animation named `loader_mix` while content loads. To use your own, save a Lottie file as `android/app/src/main/res/raw/loader_mix.json`. A resource with that exact name overrides the SDK's.

## Step 3: Configure the iOS project

### Set the platform and build settings in the Podfile

The plugin's example app pins the platform to iOS 15.0 and adds a `post_install` hook. In `ios/Podfile`:

```ruby title="Podfile"
platform :ios, '15.0'

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    if target.name == 'Giphy'
      `xcrun -sdk iphoneos bitcode_strip -r Pods/Giphy/GiphySDK/GiphyUISDK.xcframework/ios-arm64_armv7/GiphyUISDK.framework/GiphyUISDK -o Pods/Giphy/GiphySDK/GiphyUISDK.xcframework/ios-arm64_armv7/GiphyUISDK.framework/GiphyUISDK`
    end
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
      config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
    end
  end
end
```

Then run `pod install` in the `ios` folder.

The hook does two things. The `bitcode_strip` command removes bitcode from the Giphy framework that the Genuin SDK uses, because [App Store Connect rejects uploads that contain bitcode](https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes) since Xcode 14. The two build settings apply to every pod; keep them as the example app has them.

Both the `genuin_sdk` and `GenuinSDK` podspecs allow iOS 13.0. You can lower both `15.0` values to `13.0` if you support iOS 13 and 14, but the plugin's example app is configured for 15.0.

### Add usage descriptions to Info.plist

The native iOS SDK includes recording, upload, contacts, and location flows, and iOS terminates an app that reaches a protected resource without the matching `Info.plist` key. The plugin's example app declares none, so add the keys for the features your embeds enable:

| Key | Add it when |
| --- | --- |
| NSCameraUsageDescription | Viewers record video posts or video comments |
| NSMicrophoneUsageDescription | Viewers record video or voice comments |
| NSPhotoLibraryUsageDescription | Viewers upload from, or save videos to, their photo library |
| NSSpeechRecognitionUsageDescription | Voice comments are transcribed |
| NSContactsUsageDescription | Viewers add contacts as community or group members |
| NSLocationWhenInUseUsageDescription | Location-based community suggestions are on |
| NSUserTrackingUsageDescription | Your brand runs ads in embeds |

The wording the Genuin iOS team uses for each key is in [Integrate the Genuin iOS SDK](https://begenuin.com/developers/developer-resources/sdk/developer/genuin-sdk/native-compatibility/ios/how-to-integrate-and-power-your-brand-community-in-ios-apps).

### Enable universal links (Optional)

Genuin links use your white-labelled domain. For iOS to hand them to your app, add the **Associated Domains** capability in Xcode with `applinks:{{YOUR_DOMAIN}}`. Then send your Team ID and bundle ID to your Genuin contact, who adds your app to the `apple-app-site-association` file Genuin serves on that domain. [Handle Genuin deep links in your app](https://begenuin.com/developers/developer-resources/sdk/handle-genuin-deep-links-in-your-app) covers the request. The `AppDelegate` code that passes the link to the SDK is on the [initialization page](https://begenuin.com/developers/developer-resources/sdk/flutter-sdk-developer-reference-how-do-i-initialize-the-genuin-sdk-and-load-a-feed).

### Add a custom loader (Optional)

On iOS the loading animation is chosen at initialization. Add a Lottie JSON file to the Runner target so it's copied into the main bundle, and pass its name as `loaderName` when you call `GenuinSdkPlugin.initialize`.

## Permissions

### Android

The native SDK's manifest declares every permission it uses, and the manifest merger copies them into your app. You don't add any yourself, but they appear in your Play Console listing, so know what each one is for:

| Permission | Used for |
| --- | --- |
| INTERNET, ACCESS_NETWORK_STATE, ACCESS_WIFI_STATE, WAKE_LOCK, VIBRATE | Loading and playing video, notifications |
| POST_NOTIFICATIONS | Push notifications on Android 13 and later |
| CAMERA, RECORD_AUDIO | Recording video posts and voice comments |
| READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE (API 28 and below) | Uploading from the gallery and saving videos |
| READ_CONTACTS | Adding contacts as community and group members |
| ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION | Location-based community suggestions |
| READ_PHONE_STATE, WRITE_SETTINGS | Declared by the SDK; Genuin hasn't documented what uses them |
| com.google.android.gms.permission.AD_ID | The advertising ID for monetization. Google Play requires an advertising ID declaration in Play Console; see the Android SDK guide. |

The SDK asks for the runtime permissions when a viewer reaches the feature. The one exception is `POST_NOTIFICATIONS`, which your Flutter code requests through `firebase_messaging` as shown on the initialization page.

The SDK also declares the camera, front camera, microphone, and OpenGL ES 2.0 as **required** hardware features. Google Play hides the app from devices without them. To stay available on such devices, override the flag in your own manifest and confirm the result in Android Studio's **Merged Manifest** tab:

```xml title="AndroidManifest.xml"
<uses-feature
    android:name="android.hardware.camera"
    android:required="false"
    tools:replace="android:required" />
```

Repeat for `android.hardware.camera.front` and `android.hardware.microphone`. Recording then fails on devices without the hardware, but browsing and playback work.

### iOS

iOS has no manifest permissions. Add the usage descriptions from [Step 3](#add-usage-descriptions-to-infoplist) and iOS asks the viewer at first use.

## Troubleshooting

| Symptom | Cause | Fix |
| --- | --- | --- |
| Could not find org.bitbucket.genuindev:genuin_android_sdk:1.1.9.3 | The JitPack repository isn't declared | Add maven { url "https://jitpack.io" } to allprojects.repositories in android/build.gradle. |
| uses-sdk:minSdkVersion 21 cannot be smaller than version 24 declared in library | minSdk is below 24 | Set minSdk = 24 in android/app/build.gradle. |
| Manifest merger failed : Attribute application@label value=... is also present at [...] | The SDK's manifest sets its own android:label | Add xmlns:tools and tools:replace="android:label" to <application>. |
| Embeds render blank on Android, and Logcat shows ClassCastException: MainActivity cannot be cast to androidx.fragment.app.FragmentActivity | MainActivity extends FlutterActivity | Extend FlutterFragmentActivity. The plugin hosts the native feeds in fragments. |
| Crash when an SDK screen opens: The style on this component requires your app theme to be Theme.MaterialComponents | The app theme isn't a Material Components theme | Use Theme.MaterialComponents.Light.NoActionBar.Bridge as in Step 2. |
| Release build crashes in com.begenuin classes or feeds load empty, while the debug build works | R8 removed classes the SDK reaches by reflection, and the AAR ships no consumer rules | Add android/app/proguard-rules.pro as in Step 2. |
| pod install fails: Specs satisfying the GenuinSDK (~> 1.1.9.1) dependency were found, but they required a higher minimum deployment target | platform :ios is below 13.0 | Set platform :ios to 13.0 or higher. |
| App Store Connect rejects the upload: ITMS-90482: Invalid Executable ... contains bitcode | The Giphy framework contains bitcode | Keep the bitcode_strip command in post_install and rebuild. |

## Limitations

- **Native versions are fixed**: The plugin pins `genuin_android_sdk` 1.1.9.3 and `GenuinSDK` 1.1.9.1. You can't move to a newer native SDK from Flutter; a new plugin release is needed.
- **Production only**: There's no environment setting. The native Android SDK calls `https://api.begenuin.com`, so a key issued for another environment doesn't work.
- **`FlutterFragmentActivity`** **only**: The plugin casts the launcher activity to `FragmentActivity`; a plain `FlutterActivity` renders empty embeds.
- **Required hardware**: Camera, front camera, and microphone are required features unless you override them as shown in [Permissions](#permissions).
- **Portrait phones**: The SDK isn't laid out for landscape or tablets.
- **Size**: The Android AAR is 21 MB before its dependencies, which include FFmpegKit, MediaPipe, CameraX, Media3, and Giphy. The iOS pod brings FFmpegKit, MediaPipe, Giphy, and the Google IMA SDK. On Android, build with `flutter build appbundle` or `--split-per-abi` to keep downloads small.
- **GPL component**: Both native SDKs link the GPL build of FFmpegKit (`ffmpeg-kit-min-gpl` 6.0).
- **No consumer ProGuard rules**: Release builds need the rules from Step 2.

## See also

- [Initialize the Genuin SDK and load a feed in Flutter](https://begenuin.com/developers/developer-resources/sdk/flutter-sdk-developer-reference-how-do-i-initialize-the-genuin-sdk-and-load-a-feed)
- [Integrate the Genuin Android SDK](https://begenuin.com/developers/developer-resources/sdk/how-to-integrate-and-power-your-brand-community-in-android-apps)
- [Integrate the Genuin iOS SDK](https://begenuin.com/developers/developer-resources/sdk/developer/genuin-sdk/native-compatibility/ios/how-to-integrate-and-power-your-brand-community-in-ios-apps)
- [Integrate the React Native SDK](https://begenuin.com/developers/developer-resources/sdk/how-do-you-integrate-the-genuin-react-native-sdk-into-your-application)
- [Generate an embed code in Brand Control Center](https://begenuin.com/developers/browse-by-products/onsite/how-do-brands-generate-embed-codes-from-brand-control-center)
- [White-label your hosted social experience](https://begenuin.com/developers/browse-by-products/onsite/how-can-you-white-label-your-community-under-your-own-domain)
