Storyteller personalises content, tracking, and follow state per end user. The Flutter SDK mirrors the native capabilities exposed by the iOS and Android libraries.
If you also need to configure tracking behavior for privacy compliance, pass eventTrackingOptions during initialization. See Privacy and Tracking for details.
The Showcase StorytellerService.initialize persists the API key and externalId, then reuses them on relaunch—mirror that pattern if you need automatic reconnection.
Use key/value attributes to pass additional segmentation information into Storyteller. Attributes are persisted natively and automatically applied to subsequent sessions.
iOS forwards this to its native batch API. Android 11.6.3 exposes only
per-attribute primitives, so the Flutter bridge removes the prior keys and
sets the replacement values synchronously within the same method call. If an
Android replacement fails, the bridge restores the prior map when possible and
throws a PlatformException with code CUSTOM_ATTRIBUTES_ERROR.
Toggle personalization controls the same way the Showcase AttributeService._addValue calls setCustomAttribute, setLocale, or addFollowedCategory depending on the attribute type.
setFollowedCategories waits for native category resolution. If some IDs are
unknown, the native SDK applies the subset it can resolve. If that request
fails, the Future throws and the previous followed-category state is kept.
{"slug": "users", "page_title": "Working with Users", "page_url": "Users/", "canonical_url": "/flutter/Users/", "markdown": "# Working with Users\n\nStoryteller personalises content, tracking, and follow state per end user. The Flutter SDK mirrors the native capabilities exposed by the iOS and Android libraries.\n\n## Associate a user during initialisation\n\nProvide an `externalId` when calling `Storyteller.initialize`. This identifier usually maps to your own user record.\n\n```dart\nfinal result = await Storyteller.initialize(\n 'YOUR_API_KEY',\n externalId: currentUser.id,\n);\n```\n\nIf you also need to configure tracking behavior for privacy compliance, pass `eventTrackingOptions` during initialization. See [Privacy and Tracking](PrivacyAndTracking.md) for details.\n\nThe Showcase [`StorytellerService.initialize`](https://github.com/getstoryteller/storyteller-showcase-flutter/blob/main/lib/services/storyteller_service.dart#L73) persists the API key and `externalId`, then reuses them on relaunch\u2014mirror that pattern if you need automatic reconnection.\n\n## Update custom attributes\n\nUse key/value attributes to pass additional segmentation information into Storyteller. Attributes are persisted natively and automatically applied to subsequent sessions.\n\n```dart\nawait Storyteller.setCustomAttribute('subscription-tier', 'gold');\nawait Storyteller.setCustomAttribute('favorite-team', 'city-fc');\n\nfinal attributes = await Storyteller.customAttributes();\ndebugPrint('Current attributes: $attributes');\n\nawait Storyteller.removeCustomAttribute('favorite-team');\n```\n\nTo replace the full attribute map in one Flutter bridge operation, use\n`setCustomAttributes`. An empty map clears all attributes.\n\n```dart\nawait Storyteller.setCustomAttributes({\n 'subscription-tier': 'gold',\n 'favorite-team': 'city-fc',\n});\n```\n\niOS forwards this to its native batch API. Android 11.6.3 exposes only\nper-attribute primitives, so the Flutter bridge removes the prior keys and\nsets the replacement values synchronously within the same method call. If an\nAndroid replacement fails, the bridge restores the prior map when possible and\nthrows a `PlatformException` with code `CUSTOM_ATTRIBUTES_ERROR`.\n\nToggle personalization controls the same way the Showcase [`AttributeService._addValue`](https://github.com/getstoryteller/storyteller-showcase-flutter/blob/main/lib/services/attribute_service.dart#L235) calls `setCustomAttribute`, `setLocale`, or `addFollowedCategory` depending on the attribute type.\n\n## Handle locale changes\n\nIf your app allows users to switch language independently from the device, inform Storyteller by overriding the locale:\n\n```dart\nawait Storyteller.setLocale('fr-FR');\n\n// Clear the override and return locale selection to the native SDK.\nawait Storyteller.setLocale(null);\n```\n\n## Follow categories explicitly\n\nManipulate follow state in response to profile changes, onboarding, or saved preferences:\n\n```dart\nawait Storyteller.addFollowedCategories(['travel', 'music']);\n\nfinal isSportsFollowed = await Storyteller.isCategoryFollowed('sports');\nif (!isSportsFollowed) {\n await Storyteller.addFollowedCategory('sports');\n}\n\nawait Storyteller.removeFollowedCategory('travel');\nfinal followed = await Storyteller.followedCategories();\n```\n\nYou can also remove several categories at once or replace the whole set:\n\n```dart\nawait Storyteller.removeFollowedCategories(['travel', 'music']);\nawait Storyteller.setFollowedCategories(['sports', 'news']);\n```\n\n`setFollowedCategories` waits for native category resolution. If some IDs are\nunknown, the native SDK applies the subset it can resolve. If that request\nfails, the `Future` throws and the previous followed-category state is kept.\n", "copy_markdown_include_header": false, "base_path": "", "ai_dir": "ai", "missing_payload_behavior": "empty"}