join_stories_flutter 1.0.0
join_stories_flutter: ^1.0.0 copied to clipboard
JOIN Stories Flutter SDK - Monolithic plugin for JOIN Stories
JOIN Stories Flutter Plugin #
A Flutter plugin to integrate JOIN Stories widgets and standalone player in Flutter apps.
🚀 Features #
- Bubble, Card List, Card Grid widgets
- Unified standalone player API (
JOINStories.startPlayer
) - Centralized event routing (triggers, player, analytics)
- Refresh support via controllers (Bubble/Card)
- Custom fonts on Android and iOS
- iOS and Android support
Compatibility #
- Flutter: 3.0.0+
- Dart SDK: 2.17.0+
- iOS: 12.0+
- Android: API 21+
See COMPATIBILITY.md and COMPATIBILITY_SUMMARY.md for details.
Installation #
Add to your app’s pubspec.yaml
:
dependencies:
join_stories_flutter: ^0.0.1
iOS #
- Set platform to 12.0+ in
ios/Podfile
:
platform :ios, '12.0'
- Then run in
ios/
:pod repo update && pod install
Android #
- No special setup required.
Initialization #
import 'package:join_stories_flutter/join_stories_flutter.dart';
await JOINStories.initialize(teamId: 'your-team-id');
// optional if provided by JOIN
await JOINStories.initialize(teamId: 'your-team-id', apiKey: 'your-api-key');
Widgets #
import 'package:flutter/material.dart';
import 'package:join_stories_flutter/join_stories_flutter.dart';
class MyStoriesPage extends StatefulWidget {
const MyStoriesPage({super.key});
@override
State<MyStoriesPage> createState() => _MyStoriesPageState();
}
class _MyStoriesPageState extends State<MyStoriesPage> {
final BubbleController _bubbleCtrl = BubbleController();
final CardController _cardListCtrl = CardController();
final CardController _cardGridCtrl = CardController();
Future<void> _pullToRefresh() async {
await _bubbleCtrl.refresh();
await _cardListCtrl.refresh();
await _cardGridCtrl.refresh();
}
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: _pullToRefresh,
child: ListView(
children: [
BubbleWidget(
alias: 'bubble-alias',
controller: _bubbleCtrl,
configuration: const BubbleConfiguration(
showLabel: true,
labelFontSize: 14,
fontName: 'Avenir',
// player customization available here as well
),
onTriggerFetchSuccess: (count) {},
onTriggerFetchEmpty: () {},
onTriggerFetchError: (msg) {},
onPlayerFetchSuccess: () {},
onPlayerLoaded: () {},
onPlayerFetchError: (msg) {},
onPlayerDismissed: (type) {}, // 'auto' | 'manual'
onContentLinkClick: (link) {},
onAnalyticsEvent: (name, payload) {},
),
CardListWidget(
alias: 'card-list-alias',
controller: _cardListCtrl,
configuration: const CardConfiguration(
showLabel: true,
cardRadius: 12,
),
),
CardGridWidget(
alias: 'card-grid-alias',
controller: _cardGridCtrl,
configuration: const CardConfiguration(
numberOfColumns: 2,
cardSize: 150,
),
),
],
),
);
}
}
Standalone Player #
Single unified method (customization + callbacks):
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) {},
onContentLinkClick: (link) {},
onAnalyticsEvent: (eventName, payload) {},
);
Analytics & Identification #
await JOINStories.setTrackingUserId('user_123');
await JOINStories.sendConversion('purchase', 'ecommerce');
await JOINStories.setSegmentationKey('premium_user');
All analytics events are routed to Flutter via:
onAnalyticsEvent(String eventName, Map<String, dynamic> payload)
Note (iOS widgets): until per-widget IDs are available, analytics include viewId: -1
(global). The plugin broadcasts to mounted widgets so callbacks still fire.
Custom Fonts #
JOIN widgets are native views. Make the font available on both Flutter and native sides.
Flutter #
Add fonts under assets/fonts/
and declare in pubspec.yaml
:
flutter:
uses-material-design: true
fonts:
- family: Parisienne
fonts:
- asset: assets/fonts/Parisienne-Regular.ttf
Optional global theme:
MaterialApp(theme: ThemeData(fontFamily: 'Parisienne'))
iOS #
- Copy the font into
ios/Runner/Fonts/Parisienne-Regular.ttf
(Target Membership: Runner) - Add in
ios/Runner/Info.plist
underUIAppFonts
:
<string>Fonts/Parisienne-Regular.ttf</string>
- Use the exact PostScript name in JOIN configs:
fontName: 'Parisienne-Regular'
Android #
- Either put the font under
android/app/src/main/res/font/
or keep it in Flutter assets. - Pass the same
fontName
as iOS; the bridge will try assets first, thenres/font
, then system font.
Refresh Widgets #
Recommended: use controllers and call refresh()
(see example above).
Advanced: call the API directly if you have a viewId
:
await JOINStories.refreshWidget(viewId: someViewId, type: 'bubble'); // or 'card'
Example #
See the example/
directory for a complete example application.
License #
This project is licensed under the MIT License - see the LICENSE file for details.