UI Customizations

JoinStories SDK offers different ways to display your widget. There are currently two available triggers:

  • Bubbles
  • Cards

BubbleWidget

  • alias: required identifier; not a UI customization.
PropertyTypeDefaultDescription
controllerBubbleController?nullEnables refresh() without rebuilding
configurationBubbleConfiguration?nullVisual/behavior customization (see below)

BubbleConfiguration

PropertyTypeDefaultAllowed valuesDescription
showLabelbooltrue–Show label under each bubble
labelStyleTextStyle?null–Label fontSize, fontWeight, fontFamily
labelColorColor?null–Label color override
thumbViewSpacingdouble12.0–Space between bubbles
thumbViewSizedouble74.0–Bubble diameter (px)
loaderColorsList<Color>[Color(0xFFD9222D), Color(0xFFFFA800)]–Loader gradient colors
loaderWidthdouble2.0–Loader stroke width
storyViewedIndicatorColorColor?null–Color for viewed indicator ring
showPlayButtonbooltrue–Show play icon overlay
animationTypeBubbleAnimationTypenonenone, pulseFirst bubble animation
fontNameString?null–Platform font override
labelFontSizedouble?null–Label font size override
maxStoriesint?null–Max items (−1 for all)
customParametersMap<String, String>?null–Audience targeting params
horizontalMargindouble?null–Outer horizontal margin
horizontalPaddingdouble?null–Inner horizontal padding
reorderedReadStoriesbool?null–Move read stories
playerBackgroundColorColor?null–Player background color
playerVerticalAnchorString?nulltop, center, bottomPlayer vertical alignment
playerHorizontalMarginsdouble?null–Player horizontal margins
playerCornerRadiusdouble?null–Player corner radius
playerProgressBarDefaultColorColor?null–Progress bar base color
playerProgressBarFillColorColor?null–Progress bar fill color
playerProgressBarThicknessdouble?null–Progress bar thickness
playerProgressBarRadiusdouble?null–Progress bar corner radius
playerStandaloneAnimationOriginString?nulltop, topLeft, topRight, bottom, bottomLeft, bottomRightStandalone open animation origin

Example

import 'package:join_stories_flutter/join_stories_flutter.dart';

BubbleWidget(
  alias: 'widget-alias',
  configuration: const BubbleConfiguration(
    showLabel: true,
    labelColor: Color(0xFF2C3E50),
    labelFontSize: 14,
    fontName: 'Avenir',
    showPlayButton: true,
    thumbViewSpacing: 8,
    thumbViewSize: 100,
    storyViewedIndicatorColor: Color(0xFFE74C3C),
    loaderColors: [Color(0xFF3498DB), Color(0xFF2ECC71)],
    loaderWidth: 3,
    animationType: BubbleAnimationType.pulse,
    reorderedReadStories: true,
    maxStories: 5,
    horizontalMargin: 16,
    horizontalPadding: 8,

    // Player (standalone) styling used when opening the player from this widget
    playerBackgroundColor: Color(0xFF2C3E50),
    playerVerticalAnchor: 'center',
    playerHorizontalMargins: 20.0,
    playerCornerRadius: 12.0,
    playerProgressBarDefaultColor: Color(0xFF7F8C8D),
    playerProgressBarFillColor: Color(0xFF3498DB),
    playerProgressBarThickness: 4.0,
    playerProgressBarRadius: 8.0,
    playerStandaloneAnimationOrigin: 'top',
  ),

  // Trigger + Player + Analytics callbacks
  onTriggerFetchSuccess: (count) {},
  onTriggerFetchEmpty: () {},
  onTriggerFetchError: (message) {},
  onPlayerFetchSuccess: () {},
  onPlayerLoaded: () {},
  onPlayerFetchError: (message) {},
  onPlayerDismissed: (type) {}, // 'auto' | 'manual'
  onContentLinkClick: (link) {},
  onAnalyticsEvent: (name, payload) {},
);

CardWidget

  • alias: required identifier; not a UI customization.
PropertyTypeDefaultAllowed valuesDescription
isGridboolfalse–Grid when true; list when false
controllerCardController?null–Enables refresh() without rebuilding
configurationCardConfiguration?null–Visual/behavior customization (see below)

CardConfiguration

PropertyTypeDefaultAllowed valuesDescription
cardRadiusdouble8.0–Card corner radius
cardElevationdouble2.0–Card elevation
showPlayButtonbooltrue–Play icon overlay
showLabelbooltrue–Show label text
labelStyleTextStyle?null–Label fontSize, fontWeight, fontFamily
labelColorColor?null–Label color override
showOverlaybooltrue–Bottom overlay visibility
spacingdouble6.0–Space between cards
borderColorColor?null–Card border color
borderWidthint0–Card border width
storyViewedBorderColorColor?null–Border color when viewed
numberOfColumnsint?null–Grid columns (grid mode only)
cardSizedouble?null–Fixed size; grid=list height, list=width
horizontalMargindouble0.0–Outer horizontal margin
horizontalPaddingdouble0.0–Inner horizontal padding
animationTypeCardAnimationTypenonenone, pulseFirst card animation
fontNameString?null–Platform font override
labelFontSizedouble?null–Label font size override
maxStoriesint?null–Max items (−1 for all)
customParametersMap<String, String>?null–Audience targeting params
reorderedReadStoriesbool?null–Move read stories
playerBackgroundColorColor?null–Player background color
playerVerticalAnchorString?nulltop, center, bottomPlayer vertical alignment
playerHorizontalMarginsdouble?null–Player horizontal margins
playerCornerRadiusdouble?null–Player corner radius
playerProgressBarDefaultColorColor?null–Progress bar base color
playerProgressBarFillColorColor?null–Progress bar fill color
playerProgressBarThicknessdouble?null–Progress bar thickness
playerProgressBarRadiusdouble?null–Progress bar corner radius
playerStandaloneAnimationOriginString?nulltop, topLeft, topRight, bottom, bottomLeft, bottomRightStandalone open animation origin

Example

// Card List
CardListWidget(
  alias: 'widget-alias',
  configuration: const CardConfiguration(
    showLabel: true,
    labelColor: Color(0xFF8E44AD),
    labelFontSize: 16,
    fontName: 'Avenir',
    showPlayButton: true,
    cardElevation: 4,
    cardRadius: 12,
    showOverlay: true,
    spacing: 12,
    borderWidth: 2,
    borderColor: Color(0xFF9B59B6),
    storyViewedBorderColor: Color(0xFFE67E22),
    animationType: CardAnimationType.pulse,
    reorderedReadStories: false,
    maxStories: 3,

    // Optional player styling when launching from the card
    playerBackgroundColor: Color(0xFF8E44AD),
    playerVerticalAnchor: 'bottom',
    playerHorizontalMargins: 15.0,
    playerCornerRadius: 8.0,
    playerProgressBarDefaultColor: Color(0xFF95A5A6),
    playerProgressBarFillColor: Color(0xFFE74C3C),
    playerProgressBarThickness: 3.0,
    playerProgressBarRadius: 6.0,
    playerStandaloneAnimationOrigin: 'bottomRight',
  ),
  // same callbacks as BubbleWidget (triggers, player, links, analytics)
);

// Grid Card
CardGridWidget(
  alias: 'widget-alias',
  configuration: const CardConfiguration(
    showLabel: true,
    labelColor: Color(0xFF27AE60),
    labelFontSize: 18,
    fontName: 'Avenir',
    showPlayButton: true,
    cardElevation: 6,
    cardRadius: 16,
    showOverlay: true,
    spacing: 16,
    numberOfColumns: 2,
    cardSize: 150,
    borderWidth: 3,
    borderColor: Color(0xFF2ECC71),
    storyViewedBorderColor: Color(0xFFF39C12),
    animationType: CardAnimationType.pulse,
    reorderedReadStories: true,
    maxStories: 5,
  ),
);

Standalone player

Configuration

ParameterTypeDefaultAllowed valuesDescription
aliasString––Widget alias to load
standaloneOriginString?nulltop, topLeft, topRight, bottom, bottomLeft, bottomRightOpen animation origin
playerBackgroundColorint?null–ARGB background color
playerVerticalAnchorString?nulltop, center, bottomVertical alignment
playerHorizontalMarginsdouble?null–Horizontal margins
playerCornerRadiusdouble?null–Corner radius
playerProgressBarDefaultColorint?null–Progress bar base color (ARGB)
playerProgressBarFillColorint?null–Progress bar fill color (ARGB)
playerProgressBarThicknessdouble?null–Progress bar thickness
playerProgressBarRadiusdouble?null–Progress bar corner radius
  • Optional callbacks are also supported on JOINStories.startPlayer(...) (e.g., onPlayerLoaded, onPlayerDismissed, analytics and ecommerce callbacks), but they are event hooks rather than visual customizations.

  • Provided complete customization tables for BubbleWidget, CardWidget, and the standalone player, with types, defaults, and allowed values.

Example

await JOINStories.startPlayer(
  'widget-alias',
  standaloneOrigin: 'top', // 'top'|'topLeft'|'topRight'|'bottom'|'bottomLeft'|'bottomRight'
  playerBackgroundColor: 0xFF2C3E50,
  playerVerticalAnchor: 'center',
  playerHorizontalMargins: 20.0,
  playerCornerRadius: 12.0,
  playerProgressBarDefaultColor: 0xFF7F8C8D,
  playerProgressBarFillColor: 0xFF3498DB,
  playerProgressBarThickness: 4.0,
  playerProgressBarRadius: 8.0,
  onPlayerFetchSuccess: () {},
  onPlayerLoaded: () {},
  onPlayerFetchError: (msg) {},
  onPlayerDismissed: (type) {},  // 'auto' | 'manual'
  onContentLinkClick: (link) {},
  onAnalyticsEvent: (eventName, payload) {},
);

Custom Fonts

JOIN widgets (Bubble/Card) are native views. To ensure a custom font applies inside those native views, make it available both to Flutter and the native side.

Flutter steps (standard UI)

  • Add .ttf/.otf files under assets/fonts/.
  • Declare them in pubspec.yaml (family = name used by Flutter UI):
flutter:
  uses-material-design: true
  fonts:
    - family: Parisienne
      fonts:
        - asset: assets/fonts/Parisienne-Regular.ttf
  • Optional: apply globally in your Flutter app:
MaterialApp(
  theme: ThemeData(fontFamily: 'Parisienne'),
)

iOS (required for JOIN widgets)

  • Copy the font into your iOS project, e.g. ios/Runner/Fonts/Parisienne-Regular.ttf (ensure Target Membership to Runner).
  • Add it to ios/Runner/Info.plist:
<key>UIAppFonts</key>
<array>
  <string>Fonts/Parisienne-Regular.ttf</string>
  <!-- add other variants if needed -->
</array>
  • Use the exact PostScript name in the JOIN configurations (fontName), for example:
BubbleWidget(
  alias: '...',
  configuration: const BubbleConfiguration(
    fontName: 'Parisienne-Regular',
  ),
)

CardListWidget(
  alias: '...',
  configuration: const CardConfiguration(
    fontName: 'Parisienne-Regular',
  ),
)

Notes:

  • If the font doesn’t apply, verify the PostScript name (e.g. with macOS Font Book) and cold restart the app.

Android

  • Either:
    • a) Put the font under android/app/src/main/res/font/ (e.g. parisienne_regular.ttf), optionally define a res/font/\*.xml family.
    • b) Keep the font in Flutter assets (assets/fonts/...). The Android bridge attempts to load from flutter_assets/... and assets/fonts/... and falls back to res/font or a system font.
  • For JOIN, pass the same fontName as iOS (e.g. 'Parisienne-Regular').

Best practices:

  • Only add variants you actually use (Regular/Bold/Italic). fontName selects a specific variant; there’s no automatic weight/style mapping.
  • Always cold restart after adding/modifying fonts.


Did this page help you?