appstorys_flutter 1.0.0-alpha01
appstorys_flutter: ^1.0.0-alpha01 copied to clipboard
AppStorys Flutter SDK — display campaigns (Banner, Stories, PiP, Modal, Spin Wheel, Scratch Card, CSAT, Survey and more) in any Flutter app.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:appstorys_flutter/appstorys_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late final AppstorysFlutter _appstorys;
@override
void initState() {
super.initState();
_appstorys = AppstorysFlutter();
_init();
}
Future<void> _init() async {
try {
await _appstorys.initialize(
appId: 'f69bdccf-b20f-4938-b39e-7075d76db791',
accountId: '12a9eac5-94ee-4735-9aa6-b8a94cb8fbbb',
userId: 'yash1',
);
await _appstorys.getScreenCampaigns(
screenName: 'Home Screen Flutter',
positionList: ['widget_one', 'widget_two'],
);
} catch (e) {
debugPrint('[AppStorys] init failed: $e');
}
}
void _onLinkTap(String link) async {
final uri = Uri.tryParse(link);
if (uri != null) await launchUrl(uri, mode: LaunchMode.externalApplication);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AppStorys Example',
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
home: HomeScreen(appstorys: _appstorys, onLinkTap: _onLinkTap),
);
}
}
// ─── HomeScreen (tab host) ────────────────────────────────────────────────────
class HomeScreen extends StatefulWidget {
final AppstorysFlutter appstorys;
final void Function(String link) onLinkTap;
const HomeScreen({super.key, required this.appstorys, required this.onLinkTap});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
static const _screenNames = [
'Home Screen Flutter',
'Products',
'Profile',
'Settings',
];
static const _screenPositions = <String, List<String>>{
'Home Screen Flutter': ['widget_one', 'widget_two'],
'Products': [],
'Profile': [],
'Settings': [],
};
Future<void> _onTabTapped(int index) async {
setState(() => _selectedIndex = index);
final screen = _screenNames[index];
await widget.appstorys.getScreenCampaigns(
screenName: screen,
positionList: _screenPositions[screen] ?? [],
);
}
@override
Widget build(BuildContext context) {
// Mirror of native: Stack = [body content, overlayElements(), captureScreen()]
return Scaffold(
body: Stack(
children: [
_buildScreen(_selectedIndex),
// Equivalent of ...AppStorys.overlayElements() in the native code.
// Renders Banner, Floater, PiP, BottomSheet, Modal — all floating.
// Stories and Widget campaigns are inline inside each screen instead.
AppStorysOverlay(
appStorys: widget.appstorys,
bottomPadding: kBottomNavigationBarHeight,
onLinkTap: widget.onLinkTap,
),
// Equivalent of AppStorys.captureScreen(screenName, context).
widget.appstorys.captureScreenWidget(
screenName: _screenNames[_selectedIndex],
screenContext: context,
),
],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onTabTapped,
selectedItemColor: Colors.indigo,
unselectedItemColor: Colors.grey,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.grid_view_outlined), activeIcon: Icon(Icons.grid_view), label: 'Shop'),
BottomNavigationBarItem(icon: Icon(Icons.person_outline), activeIcon: Icon(Icons.person), label: 'Profile'),
BottomNavigationBarItem(icon: Icon(Icons.settings_outlined), activeIcon: Icon(Icons.settings), label: 'Settings'),
],
),
);
}
Widget _buildScreen(int index) {
switch (index) {
case 0: return _HomeTab(appstorys: widget.appstorys, onLinkTap: widget.onLinkTap);
case 1: return const _ShopTab();
case 2: return const _ProfileTab();
case 3: return _SettingsTab(appstorys: widget.appstorys);
default: return _HomeTab(appstorys: widget.appstorys, onLinkTap: widget.onLinkTap);
}
}
}
// ─── Home Tab ────────────────────────────────────────────────────────────────
class _HomeTab extends StatelessWidget {
final AppstorysFlutter appstorys;
final void Function(String link)? onLinkTap;
const _HomeTab({required this.appstorys, this.onLinkTap});
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 160,
floating: true,
flexibleSpace: FlexibleSpaceBar(
title: const Text('Good morning, Yash 👋'),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.indigo.shade700, Colors.indigo.shade400],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
),
),
// Inline — takes up real space and scrolls with the page.
SliverToBoxAdapter(
child: AppStorysStories(
appStorys: appstorys,
onLinkTap: onLinkTap,
),
),
SliverPadding(
padding: const EdgeInsets.all(16),
sliver: SliverList(
delegate: SliverChildListDelegate([
// Equivalent of AppStorys.widgets(position: "widget_one").
// Inline in scroll content — not floating.
AppStorysWidget(
appStorys: appstorys,
position: 'widget_one',
onTap: onLinkTap,
),
const SizedBox(height: 16),
_SectionHeader(key: const ValueKey('featured_deals'), title: 'Featured Deals'),
const SizedBox(height: 12),
SizedBox(
height: 140,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_DealCard(title: '50% Off Electronics', color: Colors.orange.shade100),
_DealCard(title: 'Buy 2 Get 1 Free', color: Colors.green.shade100),
_DealCard(title: 'Flash Sale: 6PM', color: Colors.red.shade100),
],
),
),
const SizedBox(height: 24),
// Equivalent of AppStorys.widgets(position: "widget_two").
AppStorysWidget(
appStorys: appstorys,
position: 'widget_two',
onTap: onLinkTap,
),
const SizedBox(height: 16),
_SectionHeader(key: const ValueKey('categories'), title: 'Categories'),
const SizedBox(height: 12),
Row(
key: const ValueKey('categories_row'),
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_CategoryChip(icon: Icons.phone_android, label: 'Electronics'),
_CategoryChip(icon: Icons.checkroom, label: 'Fashion'),
_CategoryChip(icon: Icons.blender, label: 'Home'),
_CategoryChip(icon: Icons.sports_soccer, label: 'Sports'),
],
),
const SizedBox(height: 24),
_SectionHeader(key: const ValueKey('trending_now'), title: 'Trending Now'),
const SizedBox(height: 12),
..._trendingProducts.map((p) => _ProductListTile(product: p)),
const SizedBox(height: kBottomNavigationBarHeight + 16),
]),
),
),
],
);
}
}
// ─── Shop Tab ─────────────────────────────────────────────────────────────────
class _ShopTab extends StatelessWidget {
const _ShopTab();
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
const SliverAppBar(title: Text('Shop'), pinned: true),
SliverPadding(
padding: const EdgeInsets.all(12),
sliver: SliverGrid(
delegate: SliverChildBuilderDelegate(
(context, i) => _ProductGridCard(product: _allProducts[i]),
childCount: _allProducts.length,
),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 0.72,
),
),
),
],
);
}
}
// ─── Profile Tab ──────────────────────────────────────────────────────────────
class _ProfileTab extends StatelessWidget {
const _ProfileTab();
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
const SliverAppBar(title: Text('My Profile'), pinned: true),
SliverList(
delegate: SliverChildListDelegate([
const SizedBox(height: 24),
Center(
child: CircleAvatar(
radius: 44,
backgroundColor: Colors.indigo.shade100,
child: const Text('YD', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Colors.indigo)),
),
),
const SizedBox(height: 12),
const Center(child: Text('Yash Demo', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold))),
const Center(child: Text('yash1@appstorys.co', style: TextStyle(color: Colors.grey))),
const SizedBox(height: 24),
_InfoTile(icon: Icons.local_shipping_outlined, title: 'My Orders', subtitle: '3 active orders'),
_InfoTile(icon: Icons.favorite_outline, title: 'Wishlist', subtitle: '12 saved items'),
_InfoTile(icon: Icons.card_giftcard_outlined, title: 'Rewards', subtitle: '480 points'),
_InfoTile(icon: Icons.location_on_outlined, title: 'Saved Addresses', subtitle: '2 addresses'),
]),
),
],
);
}
}
// ─── Settings Tab ─────────────────────────────────────────────────────────────
class _SettingsTab extends StatelessWidget {
final AppstorysFlutter appstorys;
const _SettingsTab({required this.appstorys});
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
const SliverAppBar(title: Text('Settings'), pinned: true),
SliverList(
delegate: SliverChildListDelegate([
const _SectionHeader(title: 'Account', padding: EdgeInsets.fromLTRB(16, 20, 16, 8)),
_SettingsTile(icon: Icons.notifications_outlined, title: 'Notifications'),
_SettingsTile(icon: Icons.lock_outline, title: 'Privacy & Security'),
_SettingsTile(icon: Icons.language_outlined, title: 'Language'),
const _SectionHeader(title: 'SDK Debug', padding: EdgeInsets.fromLTRB(16, 20, 16, 8)),
_SettingsTile(
icon: Icons.track_changes,
title: 'Track Test Event',
onTap: () async {
await appstorys.trackEvent(event: 'settings_opened');
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Event tracked: settings_opened')),
);
}
},
),
const _SectionHeader(title: 'Support', padding: EdgeInsets.fromLTRB(16, 20, 16, 8)),
_SettingsTile(icon: Icons.help_outline, title: 'Help Center'),
_SettingsTile(icon: Icons.info_outline, title: 'About'),
]),
),
],
);
}
}
// ─── Reusable small widgets ───────────────────────────────────────────────────
class _SectionHeader extends StatelessWidget {
final String title;
final EdgeInsets padding;
const _SectionHeader({super.key, required this.title, this.padding = EdgeInsets.zero});
@override
Widget build(BuildContext context) {
return Padding(
padding: padding,
child: Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
);
}
}
class _DealCard extends StatelessWidget {
final String title;
final Color color;
const _DealCard({required this.title, required this.color});
@override
Widget build(BuildContext context) {
return Container(
width: 180,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(Icons.local_offer, size: 28),
const Spacer(),
Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 4),
const Text('Tap to explore', style: TextStyle(fontSize: 12, color: Colors.black54)),
],
),
);
}
}
class _CategoryChip extends StatelessWidget {
final IconData icon;
final String label;
const _CategoryChip({required this.icon, required this.label});
@override
Widget build(BuildContext context) {
return Column(
children: [
CircleAvatar(
backgroundColor: Colors.indigo.shade50,
child: Icon(icon, color: Colors.indigo),
),
const SizedBox(height: 4),
Text(label, style: const TextStyle(fontSize: 11)),
],
);
}
}
class _ProductListTile extends StatelessWidget {
final _Product product;
const _ProductListTile({required this.product});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(bottom: 10),
child: ListTile(
leading: CircleAvatar(backgroundColor: Colors.indigo.shade50, child: Icon(product.icon, color: Colors.indigo)),
title: Text(product.name),
subtitle: Text(product.category, style: const TextStyle(fontSize: 12)),
trailing: Text(product.price, style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.indigo)),
),
);
}
}
class _ProductGridCard extends StatelessWidget {
final _Product product;
const _ProductGridCard({required this.product});
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
color: Colors.indigo.shade50,
alignment: Alignment.center,
child: Icon(product.icon, size: 48, color: Colors.indigo.shade300),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(product.name, style: const TextStyle(fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis),
Text(product.category, style: const TextStyle(fontSize: 11, color: Colors.grey)),
const SizedBox(height: 4),
Text(product.price, style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold)),
],
),
),
],
),
);
}
}
class _InfoTile extends StatelessWidget {
final IconData icon;
final String title;
final String subtitle;
const _InfoTile({required this.icon, required this.title, required this.subtitle});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(icon, color: Colors.indigo),
title: Text(title),
subtitle: Text(subtitle, style: const TextStyle(fontSize: 12)),
trailing: const Icon(Icons.chevron_right),
);
}
}
class _SettingsTile extends StatelessWidget {
final IconData icon;
final String title;
final VoidCallback? onTap;
const _SettingsTile({required this.icon, required this.title, this.onTap});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(icon),
title: Text(title),
trailing: const Icon(Icons.chevron_right),
onTap: onTap,
);
}
}
// ─── Mock data ────────────────────────────────────────────────────────────────
class _Product {
final String name;
final String category;
final String price;
final IconData icon;
const _Product({required this.name, required this.category, required this.price, required this.icon});
}
const _trendingProducts = [
_Product(name: 'Wireless Earbuds', category: 'Electronics', price: '₹1,299', icon: Icons.headphones),
_Product(name: 'Running Shoes', category: 'Sports', price: '₹2,499', icon: Icons.directions_run),
_Product(name: 'Smart Watch', category: 'Electronics', price: '₹4,999', icon: Icons.watch),
];
const _allProducts = [
_Product(name: 'Wireless Earbuds', category: 'Electronics', price: '₹1,299', icon: Icons.headphones),
_Product(name: 'Running Shoes', category: 'Sports', price: '₹2,499', icon: Icons.directions_run),
_Product(name: 'Smart Watch', category: 'Electronics', price: '₹4,999', icon: Icons.watch),
_Product(name: 'Denim Jacket', category: 'Fashion', price: '₹1,899', icon: Icons.checkroom),
_Product(name: 'Coffee Maker', category: 'Home', price: '₹3,199', icon: Icons.coffee),
_Product(name: 'Yoga Mat', category: 'Sports', price: '₹699', icon: Icons.sports_gymnastics),
_Product(name: 'Bluetooth Speaker', category: 'Electronics', price: '₹2,199', icon: Icons.speaker),
_Product(name: 'Backpack', category: 'Fashion', price: '₹1,099', icon: Icons.backpack),
];