Releases: Octopus-Community/octopus-sdk-flutter
Release list
v1.14.0
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.0API 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 underflutter: assets:),OctopusIconSource.bytes(...)orOctopusIconSource.base64(...)(raw ordata: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 inandroid/app/src/main/resis 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?)anddebugGetCommunityConfig()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
minSdk21 /compileSdk35.
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
overrideDefaultLocalelay 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;registerPushNotificationTokenandtrackCommunityAccessno longer crash beforeinitialize(); 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.
initializeOctopusAuthandswitchCommunityOctopusAuthnow 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
httpsURL 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
connectUserwas 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
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.2Added
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/NoSuchMethodErroratinitialize()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 setsoverrideDefaultLocale.
Example app
- New Presentation modes → Embedded Back Button scenario: the leading-icon
configurations against either container — the embeddedOctopusHomeScreenwidget or
theshowOctopusHomeScreenhelper — with a live counter ofonBackcalls. - 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
productionpush entitlement.
v1.13.1
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
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.embeddedViewgains
interceptProfileTapsandhasModifyUserHandlerfor 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 noonModifyUser, because the native Android
SDK requires that callback in SSO mode with app-managed fields. Pass
onModifyUseralongside and both platforms behave identically. - Community data.
fetchCommunityData(one shot) andcommunityDataFlow
(reactive) return anOctopusCommunityData(profileId,messageCount,
gamification), identified by exactly one ofprofileIdorclientUserId.
OctopusProfile.clientUserIdis the connected user's counterpart id, for
correlating with your own user record. Note that
OctopusGamification.scoreis alwaysnullthrough this API — the
backend does not populate it on this path. - Two new
screenDisplayedevents.OtherUserPostsScreen(profileId:)fires
on the ordinary path on both platforms, with no Unified Profile involved.
ActivityScreenis Android only — the iOS native SDK has no equivalent
event — and replacesProfileScreenonce Unified Profile is active. Do not
key cross-platform logic onActivityScreen.
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
API Changes
Breaking changes
Nothing.
Non-breaking changes
New APIs
showOctopusHomeScreen/openNotificationnow 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 optionalbottomSafeAreaInseton both methods lets you override it:iOS is unaffected. If you mount the// 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: () { /* … */ }, );
OctopusHomeScreenwidget directly (rather than via the helper), itsbottomSafeAreaInsetcontract is unchanged — keep passing the inset yourself.
v1.12.2
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 (ornullto send unsigned):OctopusSDK().showOctopusCreatePostScreen( info: CreatePostScreenInfo( prefilledPost: OctopusPrefilledPost(text: '…', image: bytes), bridgeShareTokenProvider: (fingerprint) async => await signOnBackend(fingerprint), ), );
-
OctopusHomeScreen.navBarLeadingActionnow works on Android too (was iOS-only). The optionalOctopusNavBarLeadingAction? navBarLeadingAction(close/back) renders a host-driven leading nav-bar icon on the root screen on both platforms; the tap fires youronBack. Use it to show a Close (X) affordance when hosting the community in a modal:OctopusHomeScreen( navBarLeadingAction: OctopusNavBarLeadingAction.close, onBack: () => Navigator.of(context).pop(), )
-
connectUsernow accepts atokenProvider— 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 statictokenparameter — prefertokenProvider. 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(...)— callconnectUser(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.isGuestis populated on iOS, soOctopusSDK.isUserConnectedistrueonly 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 anonBackto 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
syncFollowGroupsnow 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
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
1.12.0
New Features
- Custom API server endpoint: new
ApiServer(host, port)model and an optionalapiServerparameter oninitialize(...)andinitializeOctopusAuth(...). Route the SDK's gRPC traffic to a custom host/port over TLS. OmittingapiServer(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) andswitchCommunityOctopusAuth(apiKey, deepLink, apiServer)(Octopus Auth) disconnect the current user, clear cached data, and reinitialize against another community at runtime. Give embedded UI akey: 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.isInitialisedsynchronous getter andOctopusSDK.isInitialisedFlowStream<bool>(replays the current value to late subscribers and collapses consecutive duplicates). On iOS — which has no nativereset/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 thegroupIdwhen 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 aVoidCallbackto unregister (call it indispose); registering again replaces the previous callback (last-write-wins).refreshEntitlements(): newOctopusSDK().refreshEntitlements()returningFuture<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): newOctopusSDK.groupsStream<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.OctopusGroupis a lean model exposingid,name,isFollowed,canChangeFollowStatus,canAccess(false = visible-but-locked; route throughsetGroupAccessDeniedCallback), andcanCreateChildren. Mirrors the nativeOctopusSDK.groupspublic surface. - Connected user profile (
profile): newOctopusSDK.profileStream<OctopusProfile?>emitting the connected user'sOctopusProfile(ornullwhen not connected). Emits on every profile change — including afterrefreshEntitlements()— and replays the latest value to late subscribers.OctopusProfileexposes the user's heldentitlements(Set<String>, opaque tokens defined by the host app; display-only). Mirrors the nativeOctopusSDK.profile. setReaction(...): newOctopusSDK().setReaction(OctopusReactionKind? reaction, String postId)returningFuture<OctopusResult<void, SetReactionError>>. Sets (or removes, withnull) the connected user's reaction on any post — bridge posts and community posts.OctopusReactionKindis a sealed class (not an enum) with the const singletonsOctopusReactionKind.heart,.joy,.mouthOpen,.clap,.cry,.rage, and anOctopusUnknownReaction(serverValue)forward-compat fallback — so a reaction added by a newer backend decodes without an SDK release. Business failures are typedSetReactionErrors (SetReactionUnknownReactionError,SetReactionPostNotFoundError,SetReactionReactionError) carried byOctopusInvalidArguments; transport/auth failures surface through the orthogonalOctopusConnectionFailurebranch. Platform note: on iOS, removing a reaction withnullwhen none is set currently reports aSetReactionReactionErrorrather than the Android silent no-op.- Typed results (
OctopusResult): newOctopusResult<D, E>sealed hierarchy mirroring the native SDK —OctopusSuccess, connection failures (OctopusNoNetwork,OctopusContentUnavailable,OctopusUserNotAuthenticated,OctopusPermissionDenied,OctopusStatusError), andOctopusInvalidArguments<E>carrying typedOctopusServerErrors — with helpers (mapSuccess,mapErrors,onSuccess,onFailure,onError,getOrNull,getOrElse). Use the helpers, or pattern-match (exhaustive switches must annotateOctopusInvalidArguments<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 sealedOctopusPrefilledPostValidationError(...ContentEmptyError,...TextTooShortError,...TextTooLongError,...CtaLabelEmptyError,...CtaUrlEmptyError); empty text/image and blanktopicIdare normalised tonull, and text length is bounded to 10–5000. Image bytes are passed asUint8List(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 imageattachmentis a sealedOctopusClientPostAttachment(OctopusLocalImageAttachment(bytes)/OctopusRemoteImageAttachment(url)). All four mirror the lean intersection of the nativeClientPost/OctopusPrefilledPost/OctopusPostCTA/CreatePostScreenInfopublic surfaces; the consuming methods and editor widget land in a later release. - Bridge post API (
fetchOrCreateClientObjectRelatedPost): newOctopusSDK().fetchOrCreateClientObjectRelatedPost(ClientPost clientPost, {Future<String?> Function(String fingerprint)? tokenProvider})returningFuture<OctopusResult<OctopusPost, ClientPostError>>. Fetches the Octopus post linked to one of your app's objects, creating it fromclientPostif it doesn't exist yet (links community discussion to your content). The optionaltokenProvideris 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 (ornull).OctopusPostis the lean read view (id,reactionsasOctopusReactionCounts,commentCount,viewCount,userReactionKind). Content errors are typedClientPostErrors (ClientPostTextMissingError,ClientPostTextTooLongError,ClientPostFileEmptyError,ClientPostFileTooLargeError,ClientPostFileBadFormatError,ClientPostFileUploadError,ClientPostFileDownloadError,ClientPostMissingObjectIdError,ClientPostMissingCtaError,ClientPostUnavailableError,ClientPostNotFoundError,ClientPostAlreadyExistsError,ClientPostInvalidGroupIdError,ClientPostInvalidAuthorError,ClientPostTokenInvalidError,ClientPostTokenExpiredError,ClientPostOtherError) carried byOctopusInvalidArguments; transport/auth failures surface through the orthogonalOctopusConnectionFailurebranch. Platform note: on iOS the native SDK does not expose the specific validation error kind publicly, so content errors there surface asClientPostOtherError; Android produces the fine-grained subtypes. setNavigateToClientObjectCallback(...): register a callback invoked with theobjectIdwhen the user taps the "view object" button on a bridge post (a post created viafetchOrCreateClientObjectRelatedPostwith aviewObjectButtonText). The SDK never navigates on the user's behalf — your app opens its own article/product screen for that object. Returns aVoidCallbackto unregister (call it indispose); registering again replaces the previous callback (last-write-wins). Mirrors the native per-screenonNavigateToClientObject(Android) / globaldisplayClientObjectCallback(iOS).- Bridge post observation (
getClientObjectRelatedPostFlow): newOctopusSDK.getClientObjectRelatedPostFlow(String clientObjectId)returningStream<OctopusPost?>— observe the Octopus post linked to one of your app's objects (nulluntil it exists). The current value is replayed to a new subscriber, then the stream re-emits whenever the post changes — including right afterfetchOrCreateClientObjectRelatedPostcreates it, and on internal updates (reactions, comment count). Each subscription drives its own native observation, so observing the sameclientObjectIdfrom two places is safe; cancel the subscription to stop observing. Mirrors the nativegetClientObjectRelatedPostFlow(Android) /getClientObjectRelatedPostPublisher(iOS). - Create-post editor (
showOctopusCreatePostScreen): newOctopusSDK().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...
v1.9.1
Bug Fixes
- Late subscriber replay:
notSeenNotificationsCountandhasAccessToCommunitystreams now cache the latest native emission so that Dart listeners attached afterinitialize()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 receivingscreenDisplayedevents
Example App
- Load logo from bundled asset instead of hardcoded base64 string
- Enable custom logo display (
logoBase64) - Set
ProfileField.nicknameas app-managed field - Remove hardcoded
navBarTitlefromOctopusHomeScreen
v1.9.0
New Features
- Notification Badge Count:
Stream<int> notSeenNotificationsCountfor reactive badge updates,updateNotSeenNotificationsCount()to force refresh - Community Access / A/B Testing:
Stream<bool> hasAccessToCommunityfor reactive access state,overrideCommunityAccess(bool)to override cohort,trackCommunityAccess(bool)for analytics - URL Interception:
onNavigateToUrlcallback onOctopusHomeScreenwithUrlOpeningStrategyenum (handledByApp/handledByOctopus) - Locale Override:
overrideDefaultLocale(Locale?)to override the SDK UI language (e.g.Locale('fr'),Locale('en', 'US'), ornullto 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). UseOctopusSDK.events.listen(...)with Dart pattern matching
Breaking Changes
See MIGRATION_1_9.md for details
appManagedFieldsparameter changed fromList<String>?toList<ProfileField>?— useProfileField.nickname,.picture,.bioinstead of raw strings- Renamed
OctopusViewtoOctopusHomeScreen - Renamed
OctopusSdkFluttertoOctopusSDK - Renamed
initializeOctopusSDK()toinitialize() - Renamed
showOctopusHome()toshowOctopusHomeScreen() - Renamed
OctopusSdkFlutterPlugintoOctopusSDKFlutterPlugin(internal) - Renamed
OctopusSdkFlutterPlatformtoOctopusSDKPlatform(internal) - Renamed
MethodChannelOctopusSdkFluttertoOctopusSDKMethodChannel(internal) - Renamed
OctopusComposeWidgettoOctopusHomeScreen(Android internal) - Removed legacy
showNativeUI()andcloseNativeUI()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
SafeAreato 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.dartfile