Skip to content

Releases: Octopus-Community/octopus-sdk-flutter

v1.14.0

Choose a tag to compare

@ThomasGorisse ThomasGorisse released this 28 Sep 10:57

Minor release on the native 1.14 line, aligned with iOS 1.14.0. Native pins move to Android 1.14.1 and iOS 1.14.0. No breaking change on the Dart API: every 1.13.x app upgrades by bumping the constraint.

dependencies:
  octopus_sdk_flutter: ^1.14.0

API Changes

Breaking changes

Nothing.

Non-breaking changes

New APIs

Icon customization — OctopusTheme.icons. Replace the SDK's icons, the Unified Profile activity button, the six reaction images and the new screen-state illustrations (screenStates: emptyContent, emptyNotifications, networkError, error) with your own images. Slot names follow iOS OctopusTheme.Assets.Icons, grouped as groups, content, gamification, settings, profile, common and screenStates. Every slot is optional: an unset or unreadable image keeps the native default. Reaction images and screen-state illustrations keep their original colors; the other icons are tinted by the theme, so give them an alpha channel (PNG or WebP).

final activity = await OctopusIconSource.asset('assets/icons/activity.png');
final heart = await OctopusIconSource.asset('assets/icons/heart.png');
final empty = await OctopusIconSource.asset('assets/illustrations/empty.png');

final theme = OctopusTheme(
  icons: OctopusIcons(
    common: OctopusCommonIcons(activityButton: activity),
    content: OctopusContentIcons(
      reaction: OctopusReactionIcons(heart: heart),
    ),
    screenStates: OctopusScreenStateIcons(emptyContent: empty),
  ),
);

// Pass it wherever you already pass a theme:
// OctopusHomeScreen(theme: theme, ...), OctopusHomeContent(theme: theme, ...)
// or OctopusSDK().showOctopusCreatePostScreen(theme: theme).

Flutter divergences from iOS:

  • Images come from OctopusIconSource.asset(...) (a bundle key declared under flutter: assets:), OctopusIconSource.bytes(...) or OctopusIconSource.base64(...) (raw or data: URI). The plugin does not download remote images: fetch them in your app and pass the bytes. SVG and animations are not supported.
  • An on/off pair (OctopusIconOnOff) needs both images, otherwise the pair keeps its default.
  • A few slots are declared but not rendered by the native screens at these versions; the full list, image size guidance and Android limits are in doc/theming.md.

Deprecated APIs

Nothing.

Other

  • iOS privacy manifest. The iOS SDK now ships a PrivacyInfo.xcprivacy (Swift Package Manager and CocoaPods alike). You may need to update the App Privacy section of your App Store Connect page so it matches the data declared by the SDK.
  • Android string overrides. Nine Android SDK string resources were removed (notifications_list_empty, post_create_incentive_button1–4, post_create_incentive_button6, post_create_incentive_explanation, post_list_empty, post_list_other_user_empty). An override of one of them in android/app/src/main/res is now unused and can be deleted.
  • The Android native API breaks of 1.14 (GuestError.UserBanned, profile tab indices, new trailing parameters on native models) do not reach Flutter apps: the plugin bridges none of those surfaces.
  • Debug-only helpers debugOverrideExposeClientUserId(bool?) and debugGetCommunityConfig() were added for demo and QA builds. They are not supported public API; debugGetCommunityConfig() is unavailable on iOS through Swift Package Manager.
  • Platform floors unchanged: iOS 14.0, Android minSdk 21 / compileSdk 35.

Internal changes

Delivered by the native 1.14 SDKs, no code change in your app:

  • Who reacted: tapping the reaction counter of a post, comment or reply opens the list of members who reacted, with one tab per reaction type.
  • Comments tab on the profile: profile and activity screens show a member's comments and replies next to their posts. Always shown on the connected user's own profile; on other members' profiles (including OctopusInitialScreen.activity) when the community enables it.
  • Screen states: empty, loading and first-load error states across every list, with a Retry action on errors.
  • iOS: the installation id now survives deleting and reinstalling the app; URLs are detected in a body truncated behind "See more"; the server's own message is shown when an action is refused because of the user's rights.
  • Android: markdown now renders in feed bodies; links in feed bodies are now clickable; RTL locales set through overrideDefaultLocale lay out right-to-left with correctly ordered reaction counts; a post-created event no longer omits a published attachment; a feed load error is no longer shown twice; analytics session durations are correct; registerPushNotificationToken and trackCommunityAccess no longer crash before initialize(); stopping, switching community or re-initializing the SDK while it is still starting no longer closes the app.

Fixes in the plugin

  • Android: Octopus Auth started in SSO mode. initializeOctopusAuth and switchCommunityOctopusAuth now start the native SDK in Octopus Auth mode. iOS was not affected.
  • Android: the embedded community view no longer crashes the app when shown before initialize() has completed — for instance when Android restores your activity in a fresh process. The view stays empty and fills in by itself once the SDK is initialized.
  • Android: the post composer's status and navigation bars follow the SDK theme instead of the system dark mode.
  • iOS: profile pictures passed as an https URL are now used (they were silently dropped). The download is bounded to 10 seconds and 5 MiB; on failure the user connects without a picture.
  • iOS: base64 profile pictures with line breaks are accepted, as on Android.
  • iOS: re-initializing or switching community while a connectUser was in flight could leave the session anonymous. Fixed.
  • iOS: releasing a Flutter engine (add-to-app, engine restart) no longer leaves a pending token request hanging.
  • Repeated connectUser(tokenProvider:) calls no longer keep every token provider alive for the lifetime of the app.

Documentation

  • iOS with CocoaPods: static frameworks (use_frameworks! :linkage => :static) are required; the integration guide now documents the working fix for the gRPC conflict with Firebase Firestore.
  • The README is now a one-screen overview; the full reference lives in doc/integration-guide.md. Setup guide: https://doc.octopuscommunity.com/SDK/sso/

Example app

  • New scenarios: custom icons (Theme preset 6), Browse Groups, Community Access, Unified Profile override, and three debug-override scenarios.
  • Scenario Customize mode, per-feature toggles, Developer tools and Debug info, in line with the other Octopus samples.
  • Refreshed dark theme, 3:1 control outlines in light theme, system bars that follow the app theme, notification permission asked after SDK initialization, and several layout fixes on Android's gesture area.
  • iOS: deployment target raised to 15.0 for the sample only (the plugin still supports iOS 14.0).

Full Changelog: v1.13.2...v1.14.0

v1.13.2

Choose a tag to compare

@ThomasGorisse ThomasGorisse released this 06 Sep 21:30

Patch release on the native 1.13 line. Native pins move to Android 1.13.4 (from
1.13.2) and stay at iOS 1.13.2. No breaking change: one additive API on the
full-screen helpers, and every 1.13.x host upgrades by bumping the constraint to
^1.13.2.

dependencies:
  octopus_sdk_flutter: ^1.13.2

Added

onBack and navBarLeadingAction on showOctopusHomeScreen() and
openNotification().
Until now the full-screen helper popped its route and told your
app nothing, so knowing that the user had left the community meant rebuilding the route
yourself around the OctopusHomeScreen widget. Pass onBack: to be notified: the
callback runs before the helper pops the route it owns, so it must not pop anything
itself; a callback that throws is reported through FlutterError.reportError and the
route still pops. Pass navBarLeadingAction: OctopusNavBarLeadingAction.close to show
the close (X) icon instead of the back chevron the helper renders by default. Both
parameters are optional — omit them and nothing changes.

Only a tap on the SDK's root leading icon reaches onBack, on both platforms. Android
system / predictive back and the iOS swipe-from-left-edge gesture pop the Flutter route
directly; the Future the helper returns — which completes on every dismissal path —
remains the way to observe those.

await OctopusSDK.showOctopusHomeScreen(
  context,
  onNavigateToLogin: () => Navigator.of(context).pushNamed('/login'),
  navBarLeadingAction: OctopusNavBarLeadingAction.close,
  onBack: () => analytics.log('community_closed'),
);

Fixed

Android

The create-post editor no longer crashes after a process death. When Android
killed the app while the native create-post editor was in the foreground (low memory,
"Don't keep activities", another crash) and later restored it, the editor came back in
a process where the SDK had not been initialised yet and crashed on start. It now
closes itself and hands control back to your app, which re-initialises the SDK as
usual. Calling showOctopusCreatePostScreen() before initialize() now fails with a
NOT_INITIALIZED PlatformException on Android, as it already did on iOS.

Native SDK 1.13.4. Bundled through the Android dependency, no change needed in
your app:

  • No more crash when Android restores an Octopus screen — the embedded community view
    included — in a process where the SDK is not initialised yet. The native UI renders
    nothing and finishes cleanly instead.
  • No more NoClassDefFoundError / NoSuchMethodError at initialize() in apps that
    shrink their release build with R8: the SDK now ships the consumer keep rules gRPC
    needs, so you no longer have to add them yourself.
  • No more crash from the text-selection "process text" actions or the fullscreen image
    viewer when your app sets overrideDefaultLocale.

Example app

  • New Presentation modes → Embedded Back Button scenario: the leading-icon
    configurations against either container — the embedded OctopusHomeScreen widget or
    the showOctopusHomeScreen helper — with a live counter of onBack calls.
  • Visual identity aligned with the other Octopus samples: 4 tabs (Home / Scenarios /
    Community / Settings), the Debug tools moved to a modal in Settings, a Flutter badge on
    Home, corrected dark-theme accents, and the embedded SDK now themed with the sample's
    brand theme by default (a new "SDK default (no theme)" preset keeps the unthemed
    rendering reachable).
  • iOS: the Release configuration now signs against a production push entitlement.

v1.13.1

Choose a tag to compare

@ThomasGorisse ThomasGorisse released this 21 Aug 17:29

Patch release on the native 1.13 line — same pins as 1.13.0 (Android 1.13.2,
iOS 1.13.2). No breaking change: every 1.13.0 host upgrades by bumping the
constraint to ^1.13.1.

Two things make this more than a patch if you theme the SDK on iOS: the theme gains
four new keys, and a partial OctopusTheme no longer silently redefines the rest of
the theme on iOS.

API Changes

New — theme

OctopusTheme.background and OctopusTheme.link. Two optional colors wired to
both native color schemes — background is the community screens' background,
link the color of clickable links in posts and comments. Both default to null,
which keeps the native default.

OctopusTheme(
    background: Color(0xFFF7F7FB),
    link: Color(0xFF0B6BCB),
)

OctopusTheme.fontFamily and OctopusTheme.fontWeight. Applied to every SDK
text style and to the top app bar / navigation bar title. fontWeight is a plain
100–900 int and needs no native setup — an out-of-range value is now rejected by
the constructor instead of reaching Android as a crash.

fontFamily is not read from the Flutter asset bundle. Registering the font with
Flutter is not enough: it must also be registered natively under the exact same name
— a font resource in android/app/src/main/res/font/ on Android, and the PostScript
name declared under UIAppFonts in ios/Runner/Info.plist on iOS. An unregistered
name logs a warning on both platforms and falls back to the SDK's default font.

OctopusTheme(
    fontFamily: 'Inter',   // must also be registered natively, same name
    fontWeight: 600,
)

One rough edge kept as-is in this release: setting fontFamily or fontWeight also
drops the Android navigation-bar title to the size of fontSizeBody1, which is
smaller than the title size applied with no font override. fontSizeBody1 controls
the resulting size.

OctopusTheme.fontSizeNavBarItem — iOS only. The native iOS theme has a
dedicated navBarItem font slot; the native Android typography has no counterpart,
so the Android bridge documents the key as ignored rather than approximating it by
resizing another slot. Left null, nav-bar items keep following fontSizeBody1. It
reaches both theme consumption paths, so it applies to showOctopusCreatePostScreen
too, not only to the embedded community view.

New — member-scoped entry points

Open one member's posts or profile directly, on the same native screens the SDK
already opens when a user taps a member inside the community:

// That member's posts-only activity screen
OctopusInitialScreen.activity(ActivityScreenInfo.clientUserId('your-own-user-id'));
OctopusInitialScreen.activity(ActivityScreenInfo.profileId('octopus-profile-id'));

// That member's read-only profile — omit the id for the connected user's own,
// editable profile, where onModifyUser fires your edit page
OctopusInitialScreen.profile(clientUserId: 'your-own-user-id');

// Standalone widget, same shape as OctopusPostDetailsScreen
OctopusProfileScreen(clientUserId: 'your-own-user-id');

The two ActivityScreenInfo constructors are mutually exclusive and take different
paths natively: clientUserId goes through the client-user-id lookup, so it needs a
community that exposes client user ids, while profileId is already resolved and
opens with no lookup. An id that does not resolve shows the empty state — it never
falls back to another member. Pointed at the connected user's own id, .activity
opens the natives' two-tab activity screen. Ids are trimmed, and a blank or
whitespace-only id counts as no id at all: .profile then opens the connected user's
own profile, and .activity — which has no own-user form — opens the main feed.
These entry points always open on the screen's own default tab.

This is the other half of Unified Profile: once your app intercepts every profile tap
with onNavigateToProfile and renders its own page, these are the entry points that
hand the user back to the SDK on purpose.

Fixed — iOS theming

An unset theme slot no longer overrides the native default. Both iOS theme
builders substituted a value for anything the host left unset, so a partial
OctopusTheme silently redefined the rest of the theme: colors became
systemBlue / white, replacing the SDK's adaptive palette with a fixed blue, and
the six font sizes became 26 / 20 / 17 / 14 / 12 / 10 — numbers that were never
the SDK's own defaults (26 / 22 / 18 / 16 / 14 / 12) and that were fixed sizes
where the native defaults are UIFontMetrics-scaled. A host setting any theme key
therefore got community text both mis-sized and frozen against the reader's Dynamic
Type setting. An unset color or font size now keeps the SDK's own default.

This also changes the pre-existing themeMode-only path: a theme carrying nothing
but themeMode used to yield a blue primary and wrapper-invented type sizes, and
now keeps the SDK's palette and type scale. If your iOS build has been compensating
for those substituted values, this release is where the compensation becomes visible
— check any screen you tuned against them.

The same six wrong sizes were still reachable through fontFamily / fontWeight.
A custom family or weight needs a concrete point size even for a slot the host left
unset, and the sizes used for that were the same wrong ones. They now repeat the
natives' own scale (26 / 22 / 18 / 16 / 14 / 12, plus 17 for navBarItem).

An explicit font size now follows the reader's text-size setting, as the Android
bridge always did and as the iOS SDK's own defaults do. Font.system(size:) renders
a fixed point size, so setting any size at all used to opt that slot out of Dynamic
Type on iOS only — on the very keys a host reaches for to make text bigger. A size
set here is a base size on both platforms, not a frozen one.

Also in this release

Three internal test affordances are bridged from the natives for QA scenarios —
OctopusSDK.debugOverrideProfileFieldsLock, debugOverrideContentOptions and
debugOverrideTermsAcceptanceMode. They are not part of the supported public API
and may change or be removed at any time. On iOS they are implemented on the
CocoaPods path only; on the Swift Package Manager path they throw a
PlatformException with UNSUPPORTED_PLATFORM, because the types they take are not
exposed as an SPM product upstream.

The example app gains a "Create Post (Bridge Share)" scenario, four presets for the
new member-scoped entry points, and a reactions scenario that walks react → change →
unreact rather than the enum.

Full changelog:
CHANGELOG.md.

v1.13.0

Choose a tag to compare

@ThomasGorisse ThomasGorisse released this 14 Aug 14:40

Pinned to the latest native releases on both platforms: Android 1.13.2 and
iOS 1.13.2. From this release on, the package's MAJOR.MINOR always matches
the MAJOR.MINOR of the native SDKs it wraps — ^1.13.0 means the native 1.13
line, and nothing else.

Full migration guide, with a before/after snippet for every item below:
MIGRATING.md.

API Changes

Breaking changes

1. connectUser now returns a result. It previously returned
Future<void> and reported success even when the connection had been
refused
— a banned user, a rejected JWT or a missing token completed exactly
like a successful connect. It now returns
Future<OctopusResult<void, ClientUserError>>, the same shape as
overrideCommunityAccess. Existing call sites keep compiling (void is a top
type), so this is silent unless you handle it:

// Before
await octopus.connectUser(userId: id, tokenProvider: mintJwt);
// assumed connected

// After
(await octopus.connectUser(userId: id, tokenProvider: mintJwt))
    .onSuccess((_) => debugPrint('connected'))
    .onFailure((f) => showError('$f'));

One iOS caveat the bridge cannot paper over: when the token exchange fails while
nothing is connected yet — the ordinary first login — the native SDK falls back
to a guest connection and returns normally, so connectUser returns
OctopusSuccess while the user browses anonymously. Android reports every
refusal. On iOS an OctopusSuccess means "the SDK is usable", not "your SSO
user is authenticated" — the connection state does tell the two apart
(isUserConnected emits false, connectionState emits
OctopusConnected(isGuest: true)).

2. SettingsAboutScreen removed from Screen. Both native SDKs removed the
"About the community" screen in 1.13.0 — its three legal links were already
duplicated in the Activity and Profile overflow menus — so neither platform
emits the event anymore. This only breaks an exhaustive switch over Screen
with no wildcard arm; drop the arm, nothing replaces it.

3. @useResult on the result-returning APIs (analyzer). Ignoring a returned
OctopusResult is now an analyzer warning instead of passing silently. No
runtime behaviour changed, but a host that runs flutter analyze as a gate can
see it turn red without anything being broken.

4. A content-less OctopusPrefilledPost is now accepted (behaviour). It
used to be rejected; OctopusPrefilledPostContentEmptyError is still declared
but never thrown.

5. bottomSafeAreaInset: 0 resolves from the mount point — Android
(behaviour). 0 now means "reserve whatever bottom padding the ambient
MediaQuery still has left", not "reserve nothing", so a full-screen mount
clears the navigation bar on edge-to-edge devices with no host configuration. No
signature, type or default changed. To reserve nothing, consume the padding:
MediaQuery.removePadding(context: context, removeBottom: true, child: …).
Two host shapes change: a Scaffold(extendBody: true) with a bottom bar now
reserves the bar height — which is the intent of the parameter — and a host that
pads rather than consumes (a Column above a fixed footer, a Stack
overlay) now over-reserves by the navigation-bar inset. Known limitation: a
widget first built while the keyboard is up resolves 0 and keeps it, since the
engine folds the inset into viewInsets and creation params are read once —
pass the inset explicitly if your host can mount that way.

6. iOS no longer double-counts bottomSafeAreaInset (behaviour). The
parameter is documented as a total, and that is how Android behaves; the iOS
bridge forwarded it to a native API that applies it additively on top of the
safe area the embedded view already sits in. For a requested R and the safe
area S the view sits in, the reserved band goes from R + S to max(R, S) —
Android stays R. Hosts that never passed the parameter are unaffected; hosts
that had compensated for the doubling should now send the total they want, i.e.
the same value they already send on Android. This also fixes a regression from
1.12.3, where showOctopusHomeScreen / openNotification sent the launching
view's raw safe area and iOS added it again. One divergence remains: with a
hardware keyboard the native SDK reuses the same scalar as its
keyboard-height threshold, so a host passing R = 70 over S = 20 with 55 pt
reported keeps its band before and drops it after. One scalar cannot carry both
meanings from the bridge side.

Non-breaking changes

New APIs

  • Unified Profile — your app handles member-profile taps. onNavigateToProfile
    on all six public entry points: the four widgets (OctopusHomeScreen,
    OctopusHomeContent, OctopusPostDetailsScreen, OctopusGroupDetailsScreen)
    and the two navigation helpers (OctopusSDK.showOctopusHomeScreen,
    OctopusSDK.openNotification). Beneath them, OctopusSDK.embeddedView gains
    interceptProfileTaps and hasModifyUserHandler for hosts that mount the
    platform view themselves. Passing the callback is the activation switch,
    and activation is an AND gate — the community must also expose client user
    ids.
    Two asymmetries worth knowing before wiring it: on iOS the native setter lives
    on the shared SDK instance, so two embedded views alive at once are
    last-mount-wins; and Android shows the activity screen's "Edit my profile"
    item even to a host that passed no onModifyUser, because the native Android
    SDK requires that callback in SSO mode with app-managed fields. Pass
    onModifyUser alongside and both platforms behave identically.
  • Community data. fetchCommunityData (one shot) and communityDataFlow
    (reactive) return an OctopusCommunityData (profileId, messageCount,
    gamification), identified by exactly one of profileId or clientUserId.
    OctopusProfile.clientUserId is the connected user's counterpart id, for
    correlating with your own user record. Note that
    OctopusGamification.score is always null through this API — the
    backend does not populate it on this path.
  • Two new screenDisplayed events. OtherUserPostsScreen(profileId:) fires
    on the ordinary path on both platforms, with no Unified Profile involved.
    ActivityScreen is Android only — the iOS native SDK has no equivalent
    event — and replaces ProfileScreen once Unified Profile is active. Do not
    key cross-platform logic on ActivityScreen.

Inherited from the native 1.13 releases

No Dart API change, but visible to your users: 24 interface languages
(15 new, including Arabic with full RTL layout), content width capped on
large screens and tablets, a "View group" entry in the post overflow menu,
a configurable community background color, and in-app browser theming.

Example app

The published example/ compiles again. From 1.12.0 through 1.12.3 the
packaging step stripped example/lib/debug/ wholesale, while two files under it
are part of the running sample — the Debug tab, and the recorder main.dart
starts at launch. Their imports survived the strip, so the example shipped on
pub.dev and here could not be built at all. The stripped boundary is now
example/lib/debug/internal/, which holds a single internal debug console.

v1.12.3

Choose a tag to compare

@thomasgorisse-octopus thomasgorisse-octopus released this 24 Jul 15:24

API Changes

Breaking changes

Nothing.

Non-breaking changes

New APIs

  • showOctopusHomeScreen / openNotification now reserve the Android system navigation-bar inset by default. When you host the community full-screen via the top-level helper on edge-to-edge Android (API 35+), the SDK's floating "Write a post" button could sit behind the system navigation bar. The helper now auto-reserves the launching view's bottom safe area, so the button clears the nav bar out of the box. A new optional bottomSafeAreaInset on both methods lets you override it:
    // Default (null) — auto-reserves the device's bottom inset:
    OctopusSDK().showOctopusHomeScreen(context, onNavigateToLogin: () { /* … */ });
    
    // Opt back into the previous edge-to-edge look:
    OctopusSDK().showOctopusHomeScreen(
      context,
      bottomSafeAreaInset: 0,
      onNavigateToLogin: () { /* … */ },
    );
    
    // Or reserve extra host bottom chrome:
    OctopusSDK().showOctopusHomeScreen(
      context,
      bottomSafeAreaInset: 24,
      onNavigateToLogin: () { /* … */ },
    );
    iOS is unaffected. If you mount the OctopusHomeScreen widget directly (rather than via the helper), its bottomSafeAreaInset contract is unchanged — keep passing the inset yourself.

v1.12.2

Choose a tag to compare

@thomasgorisse-octopus thomasgorisse-octopus released this 09 Jul 18:21

API Changes

Breaking changes

Nothing.

Non-breaking changes

New APIs

  • CreatePostScreenInfo.bridgeShareTokenProvider — sign prefilled image shares on the create-post editor. When your community forbids member pictures, a prefilled post carrying an image must be signed. The SDK computes a fingerprint of the final content and invokes your provider; return a signed JWT (or null to send unsigned):

    OctopusSDK().showOctopusCreatePostScreen(
      info: CreatePostScreenInfo(
        prefilledPost: OctopusPrefilledPost(text: '…', image: bytes),
        bridgeShareTokenProvider: (fingerprint) async => await signOnBackend(fingerprint),
      ),
    );
  • OctopusHomeScreen.navBarLeadingAction now works on Android too (was iOS-only). The optional OctopusNavBarLeadingAction? navBarLeadingAction (close / back) renders a host-driven leading nav-bar icon on the root screen on both platforms; the tap fires your onBack. Use it to show a Close (X) affordance when hosting the community in a modal:

    OctopusHomeScreen(
      navBarLeadingAction: OctopusNavBarLeadingAction.close,
      onBack: () => Navigator.of(context).pop(),
    )
  • connectUser now accepts a tokenProvider — connect with a callback the SDK invokes whenever it needs a freshly-signed JWT (on connect and on every refresh, e.g. refreshEntitlements()):

    await OctopusSDK().connectUser(
      userId: userId,
      tokenProvider: () async => await fetchFreshJwt(),
    );

Deprecated APIs

  • connectUser's static token parameter — prefer tokenProvider. A static token can't be re-minted when the SDK re-authenticates the user, so it fails once the JWT expires. Wrap it in a provider: tokenProvider: () async => token.
  • connectUserWithTokenProvider(...) — call connectUser(tokenProvider:) instead (identical behavior).

Both keep working with a deprecation hint; they will be removed in a future major version.

Other

  • iOS Swift Package Manager support — the plugin now ships for both SPM and CocoaPods. On Flutter 3.44+ (SPM enabled by default) the plugin and its native dependencies resolve via SPM automatically; CocoaPods apps are unaffected. Minimum iOS is 14.0.
  • iOS: guest sessions are now detected — OctopusConnected.isGuest is populated on iOS, so OctopusSDK.isUserConnected is true only for a fully authenticated (non-guest) user, matching Android.

Bug fixes

  • Android: the standalone create-post editor (showOctopusCreatePostScreen) now closes correctly on both the X tap and a successful publish.
  • Android: the back chevron now works when the embedded community opens directly on a post / group / create-post screen (OctopusInitialScreen, OctopusPostDetailsScreen, OctopusGroupDetailsScreen) — wire an onBack to handle it.
  • iOS: the embedded feed can now be scrolled inside a showModalBottomSheet.
  • Android: pinned bottom bars on embedded sub-screens (comment composer) now clear the system gesture area via the host's bottomSafeAreaInset.
  • Android: following a group programmatically via syncFollowGroups now persists; spurious "Invalid token" errors on clock-ahead devices are resolved.

Wrapped native SDKs

  • Android Octopus SDK 1.12.1
  • iOS Octopus SDK 1.12.6

Full Changelog: v1.12.1...v1.12.2

v1.12.1

Choose a tag to compare

@thomasgorisse-octopus thomasgorisse-octopus released this 19 Jun 12:27

1.12.1

Documentation

  • README rewritten from scratch — pub.dev landing page rebuilt around what a Flutter dev needs in the first 5 minutes: requirements table up top, three-step quick start (init → embed → connect), separate sections for theming, presentation modes, the Bridge pattern, push wiring, and a scannable streams table. Catalogs the rest of the public surface with depth-links to doc.octopuscommunity.com. All snippets verified against the public API.

No code changes — octopus_sdk_flutter 1.12.1 ships the exact same Dart, Android, and iOS surface as 1.12.0.

v1.12.0

Choose a tag to compare

@thomasgorisse-octopus thomasgorisse-octopus released this 19 Jun 12:27

1.12.0

New Features

  • Custom API server endpoint: new ApiServer(host, port) model and an optional apiServer parameter on initialize(...) and initializeOctopusAuth(...). Route the SDK's gRPC traffic to a custom host/port over TLS. Omitting apiServer (the default) keeps the Octopus default endpoint. The host is validated at construction (ApiServerValidationError); scheme, port, path, and whitespace are rejected, bracketed/unbracketed IPv6 literals are accepted.
  • Multi-community switching: OctopusSDK().switchCommunity(apiKey, appManagedFields, apiServer) (SSO) and switchCommunityOctopusAuth(apiKey, deepLink, apiServer) (Octopus Auth) disconnect the current user, clear cached data, and reinitialize against another community at runtime. Give embedded UI a key: ValueKey(apiKey) so the native view is rebuilt for the new community.
  • SDK lifecycle: OctopusSDK().reset() disconnects the user and returns the SDK to a clean state while staying initialized; OctopusSDK().stop() tears the SDK down to an uninitialized state.
  • Initialization state: OctopusSDK.isInitialised synchronous getter and OctopusSDK.isInitialisedFlow Stream<bool> (replays the current value to late subscribers and collapses consecutive duplicates). On iOS — which has no native reset/stop/isInitialised — these are ported on top of the available native surface; reset() disconnects the user only (no public cache-clearing API on iOS).
  • setGroupAccessDeniedCallback(...): register a callback invoked with the groupId when the connected user taps a group they cannot access (locked group / follow button / detail CTA). The SDK never navigates on the user's behalf — your app decides (upsell, paywall, …). Returns a VoidCallback to unregister (call it in dispose); registering again replaces the previous callback (last-write-wins).
  • refreshEntitlements(): new OctopusSDK().refreshEntitlements() returning Future<OctopusResult<void, RefreshEntitlementsError>>. Refreshes the connected user's community entitlements from the backend (SSO mode only). Typed errors: RefreshEntitlementsNoClientTokenProviderError, RefreshEntitlementsUserNotConnectedError, RefreshEntitlementsNoNetworkError, RefreshEntitlementsUserBannedError (backend message, displayable), RefreshEntitlementsServerError.
  • Community groups (groups): new OctopusSDK.groups Stream<List<OctopusGroup>> emitting the community's groups (content categories) and re-emitting on any change (follow/unfollow, admin updates), with the latest list replayed to late subscribers and consecutive duplicates collapsed. OctopusGroup is a lean model exposing id, name, isFollowed, canChangeFollowStatus, canAccess (false = visible-but-locked; route through setGroupAccessDeniedCallback), and canCreateChildren. Mirrors the native OctopusSDK.groups public surface.
  • Connected user profile (profile): new OctopusSDK.profile Stream<OctopusProfile?> emitting the connected user's OctopusProfile (or null when not connected). Emits on every profile change — including after refreshEntitlements() — and replays the latest value to late subscribers. OctopusProfile exposes the user's held entitlements (Set<String>, opaque tokens defined by the host app; display-only). Mirrors the native OctopusSDK.profile.
  • setReaction(...): new OctopusSDK().setReaction(OctopusReactionKind? reaction, String postId) returning Future<OctopusResult<void, SetReactionError>>. Sets (or removes, with null) the connected user's reaction on any post — bridge posts and community posts. OctopusReactionKind is a sealed class (not an enum) with the const singletons OctopusReactionKind.heart, .joy, .mouthOpen, .clap, .cry, .rage, and an OctopusUnknownReaction(serverValue) forward-compat fallback — so a reaction added by a newer backend decodes without an SDK release. Business failures are typed SetReactionErrors (SetReactionUnknownReactionError, SetReactionPostNotFoundError, SetReactionReactionError) carried by OctopusInvalidArguments; transport/auth failures surface through the orthogonal OctopusConnectionFailure branch. Platform note: on iOS, removing a reaction with null when none is set currently reports a SetReactionReactionError rather than the Android silent no-op.
  • Typed results (OctopusResult): new OctopusResult<D, E> sealed hierarchy mirroring the native SDK — OctopusSuccess, connection failures (OctopusNoNetwork, OctopusContentUnavailable, OctopusUserNotAuthenticated, OctopusPermissionDenied, OctopusStatusError), and OctopusInvalidArguments<E> carrying typed OctopusServerErrors — with helpers (mapSuccess, mapErrors, onSuccess, onFailure, onError, getOrNull, getOrElse). Use the helpers, or pattern-match (exhaustive switches must annotate OctopusInvalidArguments<OctopusServerError> — see MIGRATING.md).
  • Bridge create-post models: new immutable, value-equal input models for the upcoming Bridge create-post APIs (programmatic client-object posts and the prefilled post editor). OctopusPrefilledPost({String? text, Uint8List? image, String? topicId, OctopusPostCTA? cta}) validates its payload eagerly and throws a sealed OctopusPrefilledPostValidationError (...ContentEmptyError, ...TextTooShortError, ...TextTooLongError, ...CtaLabelEmptyError, ...CtaUrlEmptyError); empty text/image and blank topicId are normalised to null, and text length is bounded to 10–5000. Image bytes are passed as Uint8List (the host materialises them — the SDK does not fetch remote URLs) and are dimension-checked later in the editor, matching the native Android behaviour. OctopusPostCTA({Uri url, String label}) is the host-supplied call-to-action (invisible in the editor, attached to the published post). CreatePostScreenInfo({OctopusPrefilledPost? prefilledPost}) describes the create-post entry point. ClientPost({String objectId, String text, OctopusClientPostAttachment? attachment, String? catchPhrase, String? viewObjectButtonText, String? groupId}) describes a post linked to one of your app's objects; its image attachment is a sealed OctopusClientPostAttachment (OctopusLocalImageAttachment(bytes) / OctopusRemoteImageAttachment(url)). All four mirror the lean intersection of the native ClientPost / OctopusPrefilledPost / OctopusPostCTA / CreatePostScreenInfo public surfaces; the consuming methods and editor widget land in a later release.
  • Bridge post API (fetchOrCreateClientObjectRelatedPost): new OctopusSDK().fetchOrCreateClientObjectRelatedPost(ClientPost clientPost, {Future<String?> Function(String fingerprint)? tokenProvider}) returning Future<OctopusResult<OctopusPost, ClientPostError>>. Fetches the Octopus post linked to one of your app's objects, creating it from clientPost if it doesn't exist yet (links community discussion to your content). The optional tokenProvider is invoked only when a new post must be created and your community requires a bridge signature: it receives the SHA-256 content fingerprint and returns a JWT signed by your backend (or null). OctopusPost is the lean read view (id, reactions as OctopusReactionCounts, commentCount, viewCount, userReactionKind). Content errors are typed ClientPostErrors (ClientPostTextMissingError, ClientPostTextTooLongError, ClientPostFileEmptyError, ClientPostFileTooLargeError, ClientPostFileBadFormatError, ClientPostFileUploadError, ClientPostFileDownloadError, ClientPostMissingObjectIdError, ClientPostMissingCtaError, ClientPostUnavailableError, ClientPostNotFoundError, ClientPostAlreadyExistsError, ClientPostInvalidGroupIdError, ClientPostInvalidAuthorError, ClientPostTokenInvalidError, ClientPostTokenExpiredError, ClientPostOtherError) carried by OctopusInvalidArguments; transport/auth failures surface through the orthogonal OctopusConnectionFailure branch. Platform note: on iOS the native SDK does not expose the specific validation error kind publicly, so content errors there surface as ClientPostOtherError; Android produces the fine-grained subtypes.
  • setNavigateToClientObjectCallback(...): register a callback invoked with the objectId when the user taps the "view object" button on a bridge post (a post created via fetchOrCreateClientObjectRelatedPost with a viewObjectButtonText). The SDK never navigates on the user's behalf — your app opens its own article/product screen for that object. Returns a VoidCallback to unregister (call it in dispose); registering again replaces the previous callback (last-write-wins). Mirrors the native per-screen onNavigateToClientObject (Android) / global displayClientObjectCallback (iOS).
  • Bridge post observation (getClientObjectRelatedPostFlow): new OctopusSDK.getClientObjectRelatedPostFlow(String clientObjectId) returning Stream<OctopusPost?> — observe the Octopus post linked to one of your app's objects (null until it exists). The current value is replayed to a new subscriber, then the stream re-emits whenever the post changes — including right after fetchOrCreateClientObjectRelatedPost creates it, and on internal updates (reactions, comment count). Each subscription drives its own native observation, so observing the same clientObjectId from two places is safe; cancel the subscription to stop observing. Mirrors the native getClientObjectRelatedPostFlow (Android) / getClientObjectRelatedPostPublisher (iOS).
  • Create-post editor (showOctopusCreatePostScreen): new OctopusSDK().showOctopusCreatePostScreen({CreatePostScreenInfo? info, OctopusTheme? theme}) method opens the native Octopus post editor (Bridge Share) presentation-style — a dedicated Activity on Android, a full-screen modal on iOS — so the editor's chrome (close button, post button, group picker) is owned b...
Read more

v1.9.1

Choose a tag to compare

@cmosa cmosa released this 20 Mar 19:56

Bug Fixes

  • Late subscriber replay: notSeenNotificationsCount and hasAccessToCommunity streams now cache the latest native emission so that Dart listeners attached after initialize() don't miss the initial value
  • screenDisplayed event parsing: Fixed type '_Map<Object?, Object?>' is not a subtype of type 'Map<String, dynamic>' crash when receiving screenDisplayed events

Example App

  • Load logo from bundled asset instead of hardcoded base64 string
  • Enable custom logo display (logoBase64)
  • Set ProfileField.nickname as app-managed field
  • Remove hardcoded navBarTitle from OctopusHomeScreen

v1.9.0

Choose a tag to compare

@cmosa cmosa released this 13 Feb 12:17

New Features

  • Notification Badge Count: Stream<int> notSeenNotificationsCount for reactive badge updates, updateNotSeenNotificationsCount() to force refresh
  • Community Access / A/B Testing: Stream<bool> hasAccessToCommunity for reactive access state, overrideCommunityAccess(bool) to override cohort, trackCommunityAccess(bool) for analytics
  • URL Interception: onNavigateToUrl callback on OctopusHomeScreen with UrlOpeningStrategy enum (handledByApp / handledByOctopus)
  • Locale Override: overrideDefaultLocale(Locale?) to override the SDK UI language (e.g. Locale('fr'), Locale('en', 'US'), or null to reset)
  • Custom Analytics: trackCustomEvent(String name, Map<String, String> properties) to send custom events to Octopus analytics
  • SDK Events: Stream<OctopusEvent> events — typed event stream covering 20 event types (content creation/deletion, reactions, polls, gamification, screen navigation, clicks, profile changes, sessions). Use OctopusSDK.events.listen(...) with Dart pattern matching

Breaking Changes

See MIGRATION_1_9.md for details

  • appManagedFields parameter changed from List<String>? to List<ProfileField>? — use ProfileField.nickname, .picture, .bio instead of raw strings
  • Renamed OctopusView to OctopusHomeScreen
  • Renamed OctopusSdkFlutter to OctopusSDK
  • Renamed initializeOctopusSDK() to initialize()
  • Renamed showOctopusHome() to showOctopusHomeScreen()
  • Renamed OctopusSdkFlutterPlugin to OctopusSDKFlutterPlugin (internal)
  • Renamed OctopusSdkFlutterPlatform to OctopusSDKPlatform (internal)
  • Renamed MethodChannelOctopusSdkFlutter to OctopusSDKMethodChannel (internal)
  • Renamed OctopusComposeWidget to OctopusHomeScreen (Android internal)
  • Removed legacy showNativeUI() and closeNativeUI() methods

Dependencies

  • Android Octopus SDK updated to 1.9.0
  • iOS Octopus SDK updated to 1.9.0

Improvements

  • Simplified callback mechanism: replaced dual callback registry with single event stream
  • SDK now initializes automatically on app start (removed manual "Init" button in example)
  • Added user connection state persistence between app restarts
  • Auto-reconnect user after SDK init if previously connected

Example App

  • Display unread notification count and community access state in SDK Status panel
  • Consolidated connect/disconnect into single conditional button
  • Added SafeArea to Configuration tab for edge-to-edge display fix
  • Shortened toast durations
  • Community tab now recreates on each tap (removed IndexedStack)
  • Moved secrets (API key, JWT token) to gitignored secrets.dart file