Initialize the Genuin SDK and load a feed in Flutter

Initialize genuin_sdk on each platform, render carousel, feed, and standard wall embeds, and connect sign-in, deep links, and push notifications.

The genuin_sdk plugin, version 1.0.1, has three parts. A native initializer on each platform starts the Genuin SDK with your API key. Four Flutter widgets host the native embed views. The GenuinSdk class sends sign-in, notification, and playback calls over a method channel named genuin_sdk.

This page documents all three. It assumes your project is configured as described in Integrate the Genuin SDK with Flutter apps: JitPack, minSdk 24, the Material theme, and the Podfile settings.

Initialize the SDK

Initialization is native code that runs once at launch, before the first Genuin widget is built. There's no Dart initializer: the GenuinSdk class has no init method, so the API key never passes through Dart.

Android

Call GenuinSdkPlugin.initSDK in onCreate of your launcher activity. The activity must extend FlutterFragmentActivity, because the plugin hosts the native feeds in fragments.

android/app/src/main/kotlin/{{YOUR_PACKAGE}}/MainActivity.kt
kotlin
import android.os.Bundle
import com.begenuin.genuin_sdk.GenuinSdkPlugin
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity : FlutterFragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        GenuinSdkPlugin.initSDK(this, "<<YOUR_API_KEY>>")
    }
}
ParameterTypeRequiredDescription
activityActivityRequiredYour FlutterFragmentActivity. The plugin passes its Application to the native SDK.
apiKeyString?RequiredYour brand's API key from Brand Control Center. With null, the plugin skips initialization and every embed stays empty.

iOS

Call GenuinSdkPlugin.initialize in AppDelegate before registering the plugins, then wrap the Flutter view controller in the navigation controller the SDK provides. Genuin's full-screen and comment views push onto that navigation controller; without it, taps inside an embed don't open anything.

ios/Runner/AppDelegate.swift
swift
import Flutter
import UIKit
import genuin_sdk

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GenuinSdkPlugin.initialize(apiKey: "<<YOUR_API_KEY>>", loaderName: "{{LOTTIE_NAME}}")
    GeneratedPluginRegistrant.register(with: self)
    if let flutterViewController = window?.rootViewController as? FlutterViewController {
      let navigationController = GenuinSdkPlugin.getGenuinNavigationController(rootViewController: flutterViewController)
      navigationController.setNavigationBarHidden(true, animated: false)
      window?.rootViewController = navigationController
      window?.makeKeyAndVisible()
    }
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
ParameterTypeRequiredDefaultDescription
apiKeyStringRequired—Your brand's API key from Brand Control Center.
showNavigationBarBoolOptionalfalsePassed to the native SDK's GenuinConfiguration.showNavigationBar. The example app leaves it false.
loaderNameString?OptionalnilName of a Lottie animation in your main bundle to use as the loading indicator. With nil, the SDK shows its own.

If your app uses a UISceneDelegate, also forward scene(_:willConnectTo:options:) to GenuinSdkPlugin.scene(_:willConnectTo:options:) so the native SDK sees the connection options.

Note: There's no environment parameter on either platform. The plugin talks to production only, so use a production API key.

Embed widgets

WidgetImportShowsPlatforms
Carouselpackage:genuin_sdk/genuin_carousel_embed_view.dartA carousel embed inside your own layoutAndroid, iOS
Feedpackage:genuin_sdk/genuin_feed_embed_view.dartA full-screen feed embedAndroid, iOS
StandardWallpackage:genuin_sdk/genuin_standard_wall_view.dartA standard wall embedAndroid, iOS
HomeFeedpackage:genuin_sdk/genuin_feed_view.dartYour brand's home feed, without an embed IDAndroid only

Each widget is a platform view: a UiKitView on iOS and a PlatformViewLink with an AndroidViewSurface on Android. Platform views have no intrinsic size, so give every widget a bounded height and width with a SizedBox, an Expanded, or a Scaffold body. A widget inside an unbounded Column throws during layout.

The embed's layout comes from its configuration in Brand Control Center. See Generate embed codes from Brand Control Center for how to create one and copy its ID.

Carousel
dart
import 'package:flutter/material.dart';
import 'package:genuin_sdk/genuin_carousel_embed_view.dart';

class CarouselSection extends StatelessWidget {
  const CarouselSection({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 300,
      width: MediaQuery.of(context).size.width,
      child: const Carousel(
        embedId: '{{EMBED_ID}}',
        uniqueId: '{{UNIQUE_ID}}',
        ssoToken: '{{SSO_TOKEN}}', // optional: signs the viewer in
      ),
    );
  }
}

The plugin's example app uses a height of 300 logical pixels. Tiles scale to the height you give.

Show a full-screen feed or standard wall

Feed and StandardWall take the same parameters as Carousel. Use the one that matches the embed's layout in Brand Control Center.

Feed and standard wall
dart
import 'package:flutter/material.dart';
import 'package:genuin_sdk/genuin_feed_embed_view.dart';

class FeedScreen extends StatelessWidget {
  const FeedScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: SafeArea(
        child: Feed(embedId: '{{EMBED_ID}}', uniqueId: '{{UNIQUE_ID}}'),
      ),
    );
  }
}

For a standard wall, import genuin_standard_wall_view.dart and replace Feed with StandardWall.

Show the brand home feed

The home feed is your brand's own feed and needs no embed ID. On Android, the HomeFeed widget renders it inline. On iOS, there's no inline widget: GenuinSdk().loadSdk() presents the SDK's home screen as a full-screen modal. loadSdk() also works on Android, where it starts the SDK's feed activity on top of your app.

Home feed
dart
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:genuin_sdk/genuin_feed_view.dart';
import 'package:genuin_sdk/genuin_sdk.dart';

class HomeFeedScreen extends StatelessWidget {
  const HomeFeedScreen({super.key});

  @override
  Widget build(BuildContext context) {
    if (Platform.isAndroid) {
      return const Scaffold(body: HomeFeed());
    }
    return Scaffold(
      body: Center(
        child: FilledButton(
          onPressed: () => GenuinSdk().loadSdk(),
          child: const Text('Open community'),
        ),
      ),
    );
  }
}

Widget parameters

Carousel, Feed, and StandardWall share one parameter set. HomeFeed takes none.

ParameterTypeRequiredDefaultDescription
embedIdStringRequired—The embed to load, from Brand Control Center.
uniqueIdStringOptional""Tells instances of the same embed apart. The plugin keys each native view by embedId plus uniqueId, so give every instance on screen a different value, or pause and resume calls hit the wrong one.
ssoTokenStringOptional""Signs the viewer in for this embed with a single sign-on token from your server. Alternatively, call ssoLogin once for the whole app.
interactionDeepLinkStringOptional""A URL to open instead of the SDK's own flow when the viewer interacts in the full-screen view. The URL must be valid; an invalid URL makes the redirect fail silently.
isDirectDeepLinkEnabledboolOptionalfalseWhen true, interactions open the specific video in your white-labelled app and interactionDeepLink is ignored. Needs a white-labelled domain and the deep-link handling below.
isDirectDeepLinkWithLoginEnabledboolOptionalfalsePassed through to the native SDK's embed configuration. Genuin hasn't documented it; leave it false.
isShowProfileEnabledboolOptionalfalseWhen true, a signed-in viewer sees their profile picture in the top-right corner of the full-screen view, with account settings and sign-out.

Parameters are read once, when the native view is created. Changing embedId or ssoToken on a widget that's already built has no effect; give the widget a new key to recreate it.

The GenuinSdk class

GenuinSdk methods
dart
import 'package:genuin_sdk/genuin_sdk.dart';

final genuin = GenuinSdk();

Every method returns a Future. The notification and playback methods take a Map<String, dynamic>; the tables below list the keys.

MethodReturnsAndroidiOSDescription
loadSdk()Future<void>YesYesOpens the SDK's home screen: a new activity on Android, a full-screen modal on iOS.
ssoLogin(String ssoToken)Future<void>YesYesSigns the viewer in for every Genuin view in the app.
ssoLogout()Future<void>YesYesSigns the viewer out of the SDK.
registerFCMToken(String token)Future<void>YesYesRegisters the device's Firebase Cloud Messaging token for Genuin notifications.
willHandleForegroundNotifications(Map)Future<bool?>YesYestrue when a notification received in the foreground is Genuin's.
handleForegroundNotifications(Map)Future<void>YesNo-opShows the Genuin notification. On iOS it returns at once; the system banner is shown by your AppDelegate.
willHandleBackgroundNotifications(Map)Future<bool?>YesYestrue when a tapped notification is Genuin's.
handleBackgroundNotifications(Map)Future<void>YesYesOpens the screen a tapped notification points to.
pauseCarousel(Map), resumeCarousel(Map)Future<void>YesThrowsPauses or resumes autoplay in a Carousel.
pauseFeed(Map), resumeFeed(Map)Future<void>YesThrowsPauses or resumes playback in a Feed.
pauseStandWall(Map), resumeStandWall(Map)Future<void>YesThrowsPauses or resumes playback in a StandardWall. Note the spelling: StandWall.
pauseHomeFeed(), resumeHomeFeed()Future<void>YesThrowsPauses or resumes the HomeFeed widget.

"Throws" means the iOS plugin has no implementation for the method, so the call fails with MissingPluginException. Guard those calls with Platform.isAndroid.

Control playback

Videos autoplay. On Android, pause them when your app goes to the background and resume them when it returns, or audio continues while the app is hidden. The map identifies the widget by the same embedId and uniqueId you gave it.

Pause and resume
dart
class _CarouselSectionState extends State<CarouselSection> with WidgetsBindingObserver {
  final _genuin = GenuinSdk();
  final _config = {'embedId': '{{EMBED_ID}}', 'uniqueId': '{{UNIQUE_ID}}'};

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (!Platform.isAndroid) return; // pause and resume are Android-only
    if (state == AppLifecycleState.paused) _genuin.pauseCarousel(_config);
    if (state == AppLifecycleState.resumed) _genuin.resumeCarousel(_config);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
}

Use pauseFeed and resumeFeed for a Feed, pauseStandWall and resumeStandWall for a StandardWall, and pauseHomeFeed and resumeHomeFeed for the HomeFeed, which needs no map.

Sign users in

Viewers can browse embeds without an account. To let them post, comment, or follow as themselves, sign them in with single sign-on. Your server issues an SSO token for the user, and the SDK creates or matches their Genuin account from it. How tokens are issued is covered in Choose how members sign in to the Genuin SDK.

You have two ways to pass the token. Set ssoToken on each widget, or call ssoLogin once after your own login completes. You don't need both.

Sign in
dart
final genuin = GenuinSdk();

// After the user signs in to your app
await genuin.ssoLogin('{{SSO_TOKEN}}');

// When the user signs out of your app
await genuin.ssoLogout();

Call ssoLogout whenever the user signs out of your app, or the Genuin session outlives yours.

Links to Genuin content use your white-labelled domain. The intent filter and the Associated Domains entitlement are described on the integration page; this section covers the code that hands an incoming link to the SDK.

With launchMode="singleTask", a link that arrives while the app is running reaches onNewIntent. Handle both the launch intent and new intents:

MainActivity.kt
kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GenuinSdkPlugin.initSDK(this, "<<YOUR_API_KEY>>")
    handleDeepLink(intent)
}

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    handleDeepLink(intent)
}

private fun handleDeepLink(intent: Intent?) {
    intent?.data?.let { GenuinSdkPlugin.handleDeepLink(this, intent) }
}

GenuinSdkPlugin.handleDeepLink(activity, intent) forwards the intent to the native SDK, which opens the matching screen.

Universal links arrive through application(_:continue:restorationHandler:). Forward the activity to the SDK, then ask the plugin to open the URL:

AppDelegate.swift
swift
override func application(
  _ application: UIApplication,
  continue userActivity: NSUserActivity,
  restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void
) -> Bool {
  let handled = GenuinSdkPlugin.flutterapplication(application, continue: userActivity, restorationHandler: restorationHandler)
  if GenuinSdkPlugin.handleDeepLink(dlURL: userActivity.webpageURL, controller: window?.rootViewController) {
    return true
  }
  return handled
}

handleDeepLink(dlURL:controller:) returns true when the SDK recognizes the URL. It then opens the destination on the given view controller after a 0.3-second delay, which gives the Flutter view time to appear on a cold start.

Handle push notifications

The SDK sends notifications for replies, mentions, and community activity through Firebase Cloud Messaging. Your app owns the Firebase integration; the plugin registers the token and handles the notifications it recognizes. For the overall flow, see Set up push notifications in the Genuin SDK.

  1. Add Firebase to the app with the FlutterFire setup, including firebase_core and firebase_messaging.
  2. Register the token after the viewer grants notification permission:
Register the token
dart
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(alert: true, badge: true, sound: true);

if (settings.authorizationStatus == AuthorizationStatus.authorized) {
  final token = await messaging.getToken();
  if (token != null) {
    await genuin.registerFCMToken(token);
  }
}
  1. Handle messages that arrive while the app is in the foreground. On Android the plugin builds the notification itself and needs body, data, and icon:
Foreground messages
dart
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
  final notificationData = {
    'title': message.notification?.title,
    'body': message.notification?.body,
    'data': message.data,
    'icon': 'mipmap/ic_notification', // or 'drawable/ic_notification'
  };
  if (await genuin.willHandleForegroundNotifications(notificationData) == true) {
    await genuin.handleForegroundNotifications(notificationData);
  } else {
    // Your own notification handling
  }
});
  1. Handle a tap on a notification, both when it opened the app and when the app was in the background:
Notification tap
dart
Future<void> openFromNotification(RemoteMessage message) async {
  final notificationData = {
    'title': message.notification?.title,
    'body': message.notification?.body,
    'data': message.data,
  };
  if (await genuin.willHandleBackgroundNotifications(notificationData) == true) {
    await genuin.handleBackgroundNotifications(notificationData);
  }
}

final initial = await FirebaseMessaging.instance.getInitialMessage();
if (initial != null) await openFromNotification(initial);
FirebaseMessaging.onMessageOpenedApp.listen(openFromNotification);
  1. On iOS, make AppDelegate the notification center delegate so foreground notifications show as banners. Add this to didFinishLaunchingWithOptions before GeneratedPluginRegistrant.register, and implement the delegate method:
AppDelegate.swift
swift
UNUserNotificationCenter.current().delegate = self
AppDelegate.swift
swift
override func userNotificationCenter(
  _ center: UNUserNotificationCenter,
  willPresent notification: UNNotification,
  withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
  completionHandler([.banner, .list, .badge, .sound])
}

Notification map keys

KeyTypeAndroidiOSDescription
dataMap<String, String>RequiredRequiredmessage.data. The SDK decides from this payload whether the notification is its own.
bodyString?Required by the foreground methodsIgnoredmessage.notification?.body, shown as the notification text.
titleString?OptionalIgnoredmessage.notification?.title.
iconStringRequired by handleForegroundNotificationsIgnoredThe small notification icon as folder/name, for example mipmap/ic_notification. Put the image in android/app/src/main/res/ under that folder. If the resource isn't found, the plugin falls back to a system icon.

On Android, a foreground map without data and body makes willHandleForegroundNotifications return false. A map without icon makes handleForegroundNotifications fail on the native side; the Dart call returns normally and no notification is shown.

Troubleshooting

SymptomCauseFix
MissingPluginException(No implementation found for method pauseCarousel on channel genuin_sdk) on iOSThe pause and resume methods exist on Android onlyGuard the calls with Platform.isAndroid.
HomeFeed renders nothing on iOSThe iOS plugin registers no view factory for genuin_feed_viewCall GenuinSdk().loadSdk() on iOS instead.
Tapping a tile on iOS opens nothingThe root view controller isn't the navigation controller from getGenuinNavigationControllerWrap the Flutter view controller as shown in Initialize the SDK.
RenderAndroidView object was given an infinite size during layout or the iOS equivalentThe widget sits in an unbounded parent, such as a Column without ExpandedGive the widget a bounded height with SizedBox or Expanded.
Embeds stay empty on Android with no errorinitSDK ran with a null key, or the activity isn't a FlutterFragmentActivityPass the key as a non-null string and extend FlutterFragmentActivity.
A foreground notification never appears on AndroidThe map is missing body or iconPass data, body, and icon as in the notification map keys.
GenuinSdk calls made from the Firebase background handler have no effectThe background handler runs in a separate isolate where the plugin has no activityCall the plugin from the main isolate, for example in onMessageOpenedApp or after getInitialMessage.
Pause or resume affects the wrong embedTwo widgets share the same embedId and uniqueIdGive each instance its own uniqueId, and pass the same pair in the map.

Limitations

  • No events or callbacks: The plugin exposes no streams. The analytics, player, and interaction callbacks of the native SDKs aren't available in Dart.
  • Production only: Neither initializer takes an environment. There's no way to point the plugin at another environment.
  • Playback control is Android-only: pause* and resume* throw MissingPluginException on iOS. The iOS views manage their own playback.
  • HomeFeed is Android-only: On iOS, use loadSdk(), which presents a full-screen modal rather than an inline view.
  • One parameter set: There are no layout setters (tile spacing, insets, corner radius), no user or contextual parameters, and no placements. Those exist in the native SDKs 2.1.5 and aren't wrapped.
  • Static parameters: Widget parameters are read once at creation. Recreate the widget with a new key to change them.
  • One HomeFeed at a time: The plugin keeps a reference to the last HomeFeed created; pauseHomeFeed and resumeHomeFeed act on that one.
  • Portrait phones: The native screens lock to portrait and aren't laid out for tablets.

See also

TRUST AND COMPLIANCE

© 2026 Genuin Inc.

Genuin Footer