antsomisdk 0.0.55
antsomisdk: ^0.0.55 copied to clipboard
This a an Antsomi Mobile SDK for Flutter.
example/lib/main.dart
import 'dart:io' show Platform;
import 'dart:convert';
import 'package:antsomisdk/antsomi.dart';
import 'package:antsomisdk/cdp_event.dart';
import 'package:antsomisdk/inbox_items.dart';
import 'package:antsomisdk/media_json.dart';
import 'package:antsomisdk/reader_segment.dart';
import 'package:antsomisdk/screen_observer.dart';
import 'package:antsomi_example/detail_screen.dart';
import 'package:antsomi_example/navigator_key.dart';
import 'package:antsomi_example/notification_screen.dart';
import 'package:antsomi_example/sdk.dart';
import 'package:antsomi_example/webview_screen.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'firebase_options.dart';
// import 'package:uni_links/uni_links.dart';
bool _initialURILinkHandled = false;
bool isFlutterLocalNotificationsInitialized = false;
late AndroidNotificationChannel channel;
void printError(dynamic err, [dynamic trace, dynamic message]) {}
Future<void> setupFlutterNotifications() async {
if (isFlutterLocalNotificationsInitialized) {
return;
}
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description:
'This channel is used for important notifications.', // description
importance: Importance.high,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
isFlutterLocalNotificationsInitialized = true;
}
void showFlutterNotification(RemoteMessage message) {
print('foreground message received: ${message.messageId}');
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null && !kIsWeb) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
// TODO add a proper drawable resource to android, for now using
// one that already exists in example app.
icon: 'launch_background',
),
),
);
}
}
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await setupFlutterNotifications();
showFlutterNotification(message);
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
print('Handling a background message ${message.messageId}');
}
void main() async {
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
if (!kIsWeb) {
await setupFlutterNotifications();
}
// Set the background messaging handler early on, as a named top-level function
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
if (Platform.isAndroid) {
await AntsomiSDK.instance.config(
appGroupId: 'group.flutter.example.pro',
portalId: '561236459',
propsId: '565024989',
applicationId: 'fbdfb60d-7ff6-41cd-8203-3ce029c51764',
);
} else {
await AntsomiSDK.instance.config(
appGroupId: 'group.flutter.example.pro',
portalId: '561236459',
propsId: '564994794',
applicationId: 'fbdfb60d-7ff6-41cd-8203-3ce029c51764',
);
}
// AntsomiSDK.instance.setCustomerProperties(customerId: "1998");
// AntsomiSDK.instance.resetUid();
AntsomiSDK.instance
.appInboxInit(destinationId: '8317604', audienceType: 'visitor');
// AntsomiSDK.instance.requestNotificationPermission();
AntsomiSDK.instance.enableLogging(enable: true);
try {
const gamificationToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnYW1pZmljYXRpb24tc2RrIiwic3ViIjoic3NzMTIzMTIzMTIzIiwiYXBwX2lkIjoiNTYxMjM2NDU5IiwicG9ydGFsX2lkIjo1NjEyMzY0NTksImtleV9pZCI6IjAxYTBjMWQwLWYwYWQtNzE1OC04MTQxLWEwOTBhZmZhODNiMyIsImlhdCI6MTc4OTk1ODM4MSwiZXhwIjoxNzg5OTYxOTgxfQ.n8K1-Oslk7O0Pn2bSLKhmnvswqXG44LzaF2elTF1uyQ';
await AntsomiSDK.instance
.initGamification(accessToken: gamificationToken, env: 'sandbox');
print("[Gamification] Initialized successfully in main()!");
} catch (e) {
print("[Gamification] Init error in main(): $e");
}
runApp(const MyApp());
// AntsomiSDK.instance.setHandleChangeRouteTemplate(callback: (String route) {
// print("route: $route");
// // implement logic here
// });
}, printError);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late CDPEvent myCDPEvent;
int _selectedIndex = 0;
int _badgeCount = 0;
List<String> _name = ['Home', 'Search', 'Notification', 'WebView'];
late final List<Widget> _body;
Future<void> getBadge() async {
final count = await AntsomiSDK.instance.getUnreadMessageCount();
print(count);
AntsomiSDK.instance.resetCustomer();
print("resetCustomer success");
debugPrint("[UID]: ${await AntsomiSDK.instance.getUid()}");
setState(() {
_badgeCount = count ?? 0;
});
ReaderSegment readerSegment = await AntsomiSDK.instance.getReaderSegment(email: "kkkk@gmail.com");
print("readerSegment ");
print(readerSegment.toJson());
// MediaJson mediaJson = await AntsomiSDK.instance.getMediaJson(event: event);
// print("webContents");
// print(jsonEncode(mediaJson));
}
@override
void initState() {
super.initState();
WidgetsFlutterBinding.ensureInitialized();
getBadge();
_body = [
const GamificationWidget(),
const Center(
child: Icon(
Icons.search,
size: 150,
),
),
NotificationSreen(
onReadMessage: (value) {
setState(() {
_badgeCount >= 0 ? _badgeCount = _badgeCount - value : 0;
});
},
onReceiveNewMessage: () {
setState(() {
_badgeCount = _badgeCount + 1;
});
},
onTapMessage: (p0) {
NavigatorKey.instance.currentState
?.pushNamed("/detail", arguments: p0);
},
),
// const WebViewApp(
// url:
// "https://sandbox-template.ants.vn/thanghn1/test-notification.html?v=3",
// )
];
_initURIHandler();
_incomingLinkHandler();
}
@override
void dispose() {
_streamSubscription?.cancel();
super.dispose();
}
void onPressNavBar(int index) {
setState(() {
_selectedIndex = index;
});
// final event = CDPEvent(en: "view_pageview");
// event.eventProps = {
// "location_url": "https://antsomi.com?submit_optin",
// };
// AntsomiSDK.instance.track(event: event);
AntsomiSDK.instance.trackScreen(name: _name[_selectedIndex]);
}
/// DEEP LINK HANDLER
StreamSubscription? _streamSubscription;
Future<void> _initURIHandler() async {
if (!_initialURILinkHandled) {
_initialURILinkHandled = true;
try {
final initialURI = null; //await getInitialUri();
if (initialURI != null) {
debugPrint("Initial URI received $initialURI");
if (!mounted) {
return;
}
AntsomiSDK.instance.handleDeepLink(url: initialURI);
final res = await AntsomiSDK.instance
.getMessageById(messageId: initialURI.path.split('/')[1]);
//Parse deep link if app open from another app(or web) when being terminated
WidgetsBinding.instance.addPostFrameCallback((_) {
NavigatorKey.instance.currentState
?.pushNamed("/detail", arguments: res);
});
} else {
debugPrint("Null Initial URI received");
}
} on PlatformException {
debugPrint("Failed to receive initial uri");
} on FormatException {
if (!mounted) {
return;
}
debugPrint('Malformed Initial URI received');
}
}
}
void _incomingLinkHandler() async {
if (!kIsWeb) {
if (!mounted) {
return;
}
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: NavigatorKey.instance,
navigatorObservers: [ScreenObserver()],
initialRoute: '/home',
routes: {
'/home': (context) => _homeWidget(),
},
onGenerateRoute: (settings) {
switch (settings.name) {
case '/detail':
final arguments = settings.arguments;
if (arguments is Map<String, String>) {
final resultShow = arguments;
final heading = resultShow['heading'] ?? resultShow['name'] ?? '';
final content = resultShow['content'] ?? '';
final imageUrl =
resultShow['imageURL'] ?? resultShow['image_url'] ?? '';
final time =
resultShow['ctime'] ?? resultShow['date_created'] ?? '';
final name = resultShow['name'] ?? resultShow['heading'] ?? '';
return MaterialPageRoute(
settings: settings,
builder: (context) => DetailScreen(
heading: heading,
content: content,
urlImage: imageUrl,
time: time,
name: name,
onDispose: () {},
),
);
} else if (arguments is InboxItem) {
final result = arguments;
return MaterialPageRoute(
settings: settings,
builder: (context) => DetailScreen(
heading: result.heading!,
content: result.content!,
urlImage: result.imageUrl!,
time: result.dateCreated!.toIso8601String(),
name: result.appUrl!,
onDispose: () {},
),
);
}
}
return null;
},
);
}
Scaffold _homeWidget() {
return Scaffold(
body: IndexedStack(
index: _selectedIndex,
children: _body,
),
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.black,
elevation: 0,
selectedIconTheme: const IconThemeData(color: Colors.blue, size: 28),
showUnselectedLabels: true,
selectedLabelStyle: const TextStyle(color: Colors.blue),
selectedItemColor: Colors.blue,
onTap: onPressNavBar,
currentIndex: _selectedIndex,
items: <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
label: "Home",
),
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.search), label: "Search"),
BottomNavigationBarItem(
icon: Stack(
children: [
const Icon(CupertinoIcons.bell),
Positioned(
right: 0,
child: _badgeCount != 0
? Container(
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(
minWidth: 16,
minHeight: 16,
),
child: Text(
'$_badgeCount',
style: const TextStyle(
color: Colors.white,
fontSize: 10,
),
textAlign: TextAlign.center,
),
)
: Container(),
)
],
),
label: "Notifications"),
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.cart), label: "Cart")
],
),
);
}
}
class GamificationWidget extends StatefulWidget {
const GamificationWidget({super.key});
@override
State<GamificationWidget> createState() => _GamificationWidgetState();
}
class _GamificationWidgetState extends State<GamificationWidget> {
static const String gamificationToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnYW1pZmljYXRpb24tc2RrIiwic3ViIjoic3NzMTIzMTIzMTIzIiwiYXBwX2lkIjoiNTYxMjM2NDU5IiwicG9ydGFsX2lkIjo1NjEyMzY0NTksImtleV9pZCI6IjAxYTBjMWQwLWYwYWQtNzE1OC04MTQxLWEwOTBhZmZhODNiMyIsImlhdCI6MTc4OTk1ODM4MSwiZXhwIjoxNzg5OTYxOTgxfQ.n8K1-Oslk7O0Pn2bSLKhmnvswqXG44LzaF2elTF1uyQ';
static const String gameCode = 'road-color';
bool _initialized = true;
bool _loading = false;
String _statusMessage = 'Đã gọi initGamification từ main()';
List<GamificationGame> _games = [];
@override
void initState() {
super.initState();
_handleGetListGame();
}
Future<void> _handleInit() async {
setState(() {
_loading = true;
_statusMessage = 'Đang khởi tạo Gamification...';
});
try {
await AntsomiSDK.instance
.initGamification(accessToken: gamificationToken, env: 'sandbox');
setState(() {
_initialized = true;
_statusMessage = 'Khởi tạo Gamification thành công!';
});
print("[Gamification] Manual init success");
} catch (e) {
setState(() {
_statusMessage = 'Lỗi khởi tạo: $e';
});
print("[Gamification] Manual init error: $e");
} finally {
setState(() {
_loading = false;
});
}
}
Future<void> _handleGetListGame() async {
setState(() {
_loading = true;
_statusMessage = 'Đang tải danh sách game...';
});
try {
final games = await AntsomiSDK.instance.getListGame();
setState(() {
_games = games;
_statusMessage = 'Tìm thấy ${games.length} games';
});
print("[Gamification] found ${games.length} games: $games");
} catch (e) {
setState(() {
_statusMessage = 'Lỗi lấy game: $e';
});
print("[Gamification] getListGame error: $e");
} finally {
setState(() {
_loading = false;
});
}
}
Future<void> _handlePlayGame() async {
try {
await AntsomiSDK.instance.playGame(
gameCode: gameCode,
sourceUrl:
'highlands-stg://home?utm_type=Offline&utm_source=HLC_TEST&utm_medium=POSM_TEST&utm_term=QR_TEST&utm_campaign=Highlands_Test&utm_content=Dec2025_test',
);
} catch (e) {
setState(() {
_statusMessage = 'Lỗi Play Game: $e';
});
print("[Gamification] playGame error: $e");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('🎮 Gamification Test'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _initialized ? Colors.green.shade50 : Colors.orange.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: _initialized ? Colors.green : Colors.orange,
),
),
child: Text(
'Trạng thái: $_statusMessage',
style: TextStyle(
color: _initialized ? Colors.green.shade800 : Colors.orange.shade800,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 16),
ElevatedButton.icon(
icon: const Icon(Icons.refresh),
label: Text(_loading ? 'Đang xử lý...' : '1. Re-Init Gamification'),
onPressed: _loading ? null : _handleInit,
),
const SizedBox(height: 12),
ElevatedButton.icon(
icon: const Icon(Icons.list),
label: const Text('2. Get List Game'),
onPressed: _loading ? null : _handleGetListGame,
),
const SizedBox(height: 12),
ElevatedButton.icon(
icon: const Icon(Icons.sports_esports),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
),
label: const Text('3. 🎮 Play Game (he-phieu-em-2026)'),
onPressed: _loading ? null : _handlePlayGame,
),
const SizedBox(height: 20),
if (_games.isNotEmpty) ...[
const Text(
'Danh sách Games:',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
..._games.map(
(g) => Card(
child: ListTile(
leading: g.iconUrl != null && g.iconUrl!.isNotEmpty
? Image.network(g.iconUrl!, width: 40, height: 40, errorBuilder: (_, __, ___) => const Icon(Icons.gamepad))
: const Icon(Icons.gamepad),
title: Text(g.name ?? g.gameCode ?? 'Unnamed Game'),
subtitle: Text('Code: ${g.gameCode} | Status: ${g.status}'),
trailing: IconButton(
icon: const Icon(Icons.play_arrow, color: Colors.green),
onPressed: () {
if (g.gameCode != null) {
AntsomiSDK.instance.playGame(gameCode: g.gameCode!);
}
},
),
),
),
),
],
],
),
),
);
}
}