Commit 107cdd2d authored by DatHV's avatar DatHV
Browse files

update build x-app-sdk

parent 9bb8aadd
Pipeline #2082 failed with stages
in 0 seconds
# Close App Integration với x-app-sdk
# Close App Integration
## Tổng quan
......@@ -154,6 +154,5 @@ http://localhost:8080/?token=test123&user={"id":"user123"}
- `lib/web/web_helper_web.dart` - Implementation cho web
- `lib/web/web_helper_stub.dart` - Stub cho non-web platforms
- `lib/web/x_app_sdk_service.dart` - Service gọi JavaScript
- `web/index.html` - JavaScript implementation
- `lib/web/close_app_example.dart` - Ví dụ sử dụng
......@@ -34,10 +34,6 @@
./scripts/open_browser_cors_disabled.sh
```
### **Test x-app-sdk:**
```bash
./scripts/test_x_app_sdk.sh
```
## 📁 **Cấu trúc đơn giản:**
......@@ -53,12 +49,12 @@ flutter_app_mypoint/
│ ├── switch_env.sh # Chuyển đổi môi trường
│ ├── run_web_complete.sh # Chạy web với CORS
│ ├── open_browser_cors_disabled.sh # Mở Chrome với CORS disabled
└── test_x_app_sdk.sh # Test x-app-sdk
└── lib/web/ # x-app-sdk integration
└── test_web.sh # Test web
└── lib/web/ # Web integration
```
## ✅ **Kết quả:**
-**Đơn giản**: Chỉ 2 lệnh chính
-**Dễ nhớ**: `./run_dev.sh``./run_prod.sh`
-**x-app-sdk**: Tích hợp đầy đủ
-**Web**: Tích hợp đầy đủ
-**Sẵn sàng**: Chạy ngay
......@@ -18,12 +18,6 @@
- `./scripts/run_web_complete.sh` - Chạy development với CORS
- `./scripts/export_and_run.sh` - Export + chạy + mở browser
#### **3. Tích hợp x-app-sdk:**
- `lib/web/x_app_sdk_service.dart` - Service chính
- `lib/web/web_helper_web.dart` - Web helper functions
- `lib/web/web_helper_stub.dart` - Stub cho non-web
- `web/index.html` - JavaScript integration
- `X_APP_SDK_INTEGRATION.md` - Hướng dẫn tích hợp
## 🚀 **Cách sử dụng đơn giản:**
......@@ -79,22 +73,6 @@ from origin 'http://localhost:8080' has been blocked by CORS policy
- Cài "CORS Unblock" extension
- Enable khi test
## 🔧 **x-app-sdk Integration:**
### **Chức năng:**
- Lấy token từ Super App: `window.getToken()`
- Lấy user info từ Super App: `window.getInfo('USER_ID')`
- Retry mechanism: Tối đa 3 lần
- Fallback: URL parameters và localStorage
### **Test:**
```bash
# Test với URL parameters
http://localhost:8080?token=abc123&userId=user456
# Test x-app-sdk
./scripts/test_x_app_sdk.sh
```
## 📁 **Cấu trúc đơn giản:**
......@@ -103,20 +81,18 @@ flutter_app_mypoint/
├── assets/config/
│ └── env.json # Config duy nhất
├── lib/web/
│ ├── x_app_sdk_service.dart # x-app-sdk service
│ ├── web_helper_web.dart # Web functions
│ └── web_helper_stub.dart # Stub functions
│ └── (web helper files)
├── web/
│ └── index.html # JavaScript integration
├── export_web.sh # Export script
└── scripts/
├── run_web_complete.sh # Development
├── export_and_run.sh # Export + run
└── test_x_app_sdk.sh # Test x-app-sdk
└── test_web.sh # Test web
```
## 🎯 **Kết quả:**
-**Đơn giản**: Chỉ 1 config file
-**Dễ hiểu**: Không còn phức tạp
-**x-app-sdk**: Tích hợp đầy đủ
-**Web**: Tích hợp đầy đủ
-**Sẵn sàng**: Export và deploy ngay
# X-App-SDK Integration Guide for Mini App
# X-App-SDK Integration Guide
## Tổng quan
Tài liệu này mô tả cách tích hợp mini app với `x-app-sdk` để lấy thông tin token, user từ Super App. Super App đã có sẵn `x-app-sdk`, mini app chỉ cần gọi các method để lấy dữ liệu.
Tài liệu này mô tả cách tích hợp mini app với `x-app-sdk` để lấy token và đóng app từ Super App. Implementation này đơn giản và chỉ sử dụng 2 API chính: `getToken()``closeApp()`.
## Cài đặt
### 1. Build Flutter web app
### 1. Install x-app-sdk
```bash
npm install x-app-sdk@^1.1.2
```
### 2. Build Flutter web app
```bash
flutter build web
......@@ -16,63 +22,37 @@ flutter build web
### Trong Super App
Super App đã có sẵn `x-app-sdk` và cung cấp các method global:
Super App không cần làm gì đặc biệt. x-app-sdk sẽ tự động detect Super App environment và sử dụng các method có sẵn.
```javascript
// Super App đã có sẵn các method này:
// window.getToken() - Lấy token đăng nhập
// window.getInfo(key) - Lấy thông tin người dùng theo key
// Mini app sẽ tự động gọi:
// window.getToken() - Lấy token
// window.getInfo('USER_ID') - Lấy ID người dùng
// window.getInfo('USER_NAME') - Lấy tên người dùng
// window.getInfo('USER_EMAIL') - Lấy email người dùng
// window.getInfo('USER_PHONE') - Lấy số điện thoại
```
**Lưu ý**: Mini app sử dụng x-app-sdk thật từ npm package, không phải mock.
### Trong Mini App (Flutter)
Mini app sẽ tự động lấy dữ liệu từ Super App:
Mini app sẽ tự động lấy token từ Super App khi khởi động:
```dart
// Lấy token
String? token = webGetAppHostToken();
// Token được lấy tự động trong splash screen
// Không cần gọi thủ công
// Lấy user info
Map<String, dynamic>? user = webGetAppHostUser();
// Kiểm tra data có sẵn
bool isReady = webIsAppHostDataReady();
// Kiểm tra x-app-sdk có sẵn từ Super App
bool sdkAvailable = webIsSDKAvailable();
// Lấy error message nếu có
String? error = webGetAppHostError();
// Lấy thông tin user theo key cụ thể
dynamic userInfo = await webGetUserInfoByKey('USER_NAME');
// Lấy token bất đồng bộ
String? token = await webGetTokenAsync();
// Đóng app và trả về Super App
webCloseApp({
'message': 'Task completed',
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
```
## API Reference
### Web Helper Functions
- `webGetAppHostToken()`: Lấy token từ Super App
- `webGetAppHostUser()`: Lấy thông tin user từ Super App
- `webIsAppHostDataReady()`: Kiểm tra data có sẵn
- `webIsSDKAvailable()`: Kiểm tra x-app-sdk có sẵn từ Super App
- `webGetAppHostError()`: Lấy error message
- `webInitializeXAppSDK()`: Khởi tạo x-app-sdk service
- `webStoreAppHostData(token, user)`: Lưu data vào localStorage
- `webClearAppHostData()`: Xóa data
- `webCallXAppSDKMethod(methodName, args)`: Gọi method của x-app-sdk
- `webGetUserInfoByKey(key)`: Lấy thông tin user theo key
- `webGetTokenAsync()`: Lấy token bất đồng bộ
- `webGetToken()`: Lấy token từ Super App
- `webCloseApp(data)`: Đóng app và trả về Super App với data
- `webIsSDKInitialized()`: Kiểm tra SDK đã khởi tạo chưa
- `webGetCachedToken()`: Lấy token đã cache
- `webGetLastError()`: Lấy error message cuối cùng
- `webClearTokenCache()`: Xóa token cache
- `webResetSDK()`: Reset SDK service
### XAppSDKService
......@@ -82,105 +62,67 @@ final service = XAppSDKService();
// Khởi tạo
await service.initialize();
// Lấy dữ liệu
String? token = service.token;
Map<String, dynamic>? user = service.user;
bool isReady = service.isReady;
String? error = service.error;
// Lưu dữ liệu
service.storeData(token, user);
// Xóa dữ liệu
service.clearData();
// Gọi method SDK trực tiếp
dynamic result = await service.callSDKMethod('getInfo', ['USER_NAME']);
// Lấy thông tin user theo key
dynamic userInfo = await service.getUserInfo('USER_EMAIL');
// Lấy token
String? token = await service.getToken();
// Lấy token bất đồng bộ
String? token = await service.getTokenAsync();
// Đóng app
await service.closeApp({'message': 'Done'});
// Kiểm tra SDK có sẵn
bool sdkAvailable = service.isSDKAvailable();
// Kiểm tra trạng thái
bool isReady = service.isInitialized;
String? cachedToken = service.cachedToken;
String? error = service.lastError;
```
## X-App-SDK Methods (từ Super App)
## Luồng hoạt động
### getToken()
Lấy token đăng nhập người dùng từ Super App.
```javascript
// Super App cung cấp method này
window.getToken().then(token => {
console.log('Token người dùng:', token);
}).catch(error => {
console.error('Lỗi lấy token:', error);
});
```
1. **Khởi tạo**: App khởi tạo x-app-sdk khi start
2. **Splash Screen**: Tự động gọi `getToken()` để lấy token
3. **Fallback**: Nếu SDK không có token, fallback về URL params
4. **Login**: Sử dụng token để đăng nhập
5. **Close App**: Khi cần đóng app, gọi `closeApp()`
### getInfo(key)
Lấy thông tin người dùng từ Super App theo key.
## Test
```javascript
// Super App cung cấp method này
// Lấy user ID
window.getInfo('USER_ID').then(info => {
console.log('ID người dùng:', info.data);
}).catch(error => {
console.error('Lỗi lấy thông tin:', error);
});
### Local Development
// Lấy thông tin khác
window.getInfo('USER_NAME').then(info => {
console.log('Tên người dùng:', info.data);
});
```bash
# Test với mock SDK
./scripts/test_x_app_sdk.sh
```
### Available Keys
- `'USER_ID'`: ID người dùng
- `'USER_NAME'`: Tên người dùng
- `'USER_EMAIL'`: Email người dùng
- `'USER_PHONE'`: Số điện thoại
## Fallback Methods
### Production
Nếu `x-app-sdk` không khả dụng, app sẽ thử các phương pháp fallback:
Không cần thay đổi gì. x-app-sdk sẽ tự động detect Super App environment và hoạt động đúng.
1. **URL Parameters**: `?token=xxx&user=yyy`
2. **localStorage**: `app_host_token`, `app_host_user`
**Lưu ý**: x-app-sdk được load từ npm package, tự động detect Super App environment.
## Debugging
Để debug, mở Developer Tools và kiểm tra:
## Troubleshooting
1. Console logs với prefix `🔍`, `✅`, `❌`, `⚠️`
2. `window.AppHostData` object
3. Network tab để xem việc load x-app-sdk
### SDK không khởi tạo được
## Lưu ý
- Kiểm tra console log: `❌ XAppSDKService: x-app-sdk not found in window`
- Đảm bảo Super App đã load x-app-sdk trước khi load mini app
- Chỉ hoạt động trên web platform
- Super App đã có sẵn `x-app-sdk` library
- Mini app gọi `window.getToken()``window.getInfo()` từ Super App
- Data được lưu trong localStorage để sử dụng lại
- App tự động listen cho updates từ Super App
- SDK với dữ liệu thật chỉ được trả ra khi chạy trên Super App
- Khi chạy trên web thường sẽ có data mẫu có cấu trúc tương tự data thật
### Token không lấy được
## Troubleshooting
- Kiểm tra console log: `❌ SplashScreen - Failed to get token from SDK`
- Fallback sẽ tự động sử dụng URL params
- Kiểm tra Super App có expose `getToken()` method không
### Lỗi thường gặp:
### CloseApp không hoạt động
1. **"x-app-sdk not available from Super App"**: Super App chưa expose methods
2. **"No data available from Super App"**: Không lấy được data từ Super App
3. **CORS errors**: Cần cấu hình CORS cho Super App
- Kiểm tra console log: `❌ XAppSDKService: closeApp method not found`
- Fallback sẽ tự động sử dụng `window.history.back()` hoặc `window.close()`
### Giải pháp:
## Files liên quan
1. Kiểm tra Super App đã expose `window.getToken()``window.getInfo()` chưa
2. Kiểm tra console logs để xem chi tiết lỗi
3. Sử dụng fallback methods nếu cần
4. Kiểm tra `webIsSDKAvailable()` để xác nhận SDK có sẵn
- `lib/web/x_app_sdk_service.dart` - Service chính
- `lib/web/web_helper_web.dart` - Web implementation
- `lib/web/web_helper_stub.dart` - Stub cho non-web platforms
- `lib/web/web_helper.dart` - Export file
- `lib/screen/splash/splash_screen_viewmodel.dart` - Tích hợp trong splash
- `lib/base/app_navigator.dart` - Sử dụng closeApp
- `lib/core/app_initializer.dart` - Khởi tạo SDK
- `web/index.html` - Mock SDK cho development
- `scripts/test_x_app_sdk.sh` - Script test
{
"flavor":"dev",
"baseUrl":"https://api.sandbox.mypoint.com.vn/8854/gup2start/rest",
"t3Token":"runner-env-flavor-dev",
"enableLogging":true
"flavor":"pro",
"baseUrl":"https://api.mypoint.com.vn/8854/gup2start/rest",
"t3Token":"runner-env-flavor-pro",
"enableLogging":false
}
......@@ -19,8 +19,8 @@ flutter clean
rm -rf .dart_tool build
flutter pub get
# Install web dependencies for x-app-sdk
echo "📦 Installing web dependencies for x-app-sdk..."
# Install web dependencies
echo "📦 Installing web dependencies..."
cd web
if [ -f "package.json" ]; then
npm install
......@@ -130,17 +130,6 @@ Có thể override bằng query parameters: `?env=production`
Firebase config được load từ `firebase_options.dart`
Đảm bảo Firebase project được cấu hình đúng cho domain này.
## x-app-sdk Integration
App được tích hợp với x-app-sdk để giao tiếp với Super App:
- **Token retrieval**: Lấy token từ Super App
- **User info**: Lấy thông tin user từ Super App
- **Fallback methods**: URL parameters và localStorage
- **Retry mechanism**: Tối đa 3 lần retry
### Test x-app-sdk:
1. Mở `test_urls.html` để test với URL parameters
2. Kiểm tra console log để debug
3. Test với Super App environment
## CORS Configuration
App cần CORS headers để gọi API:
......@@ -154,7 +143,6 @@ Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, Acc
- Nếu có lỗi CORS, cấu hình server để allow origin của domain
- Nếu có lỗi Firebase, kiểm tra domain trong Firebase console
- Nếu có lỗi assets, đảm bảo đường dẫn relative đúng
- Nếu có lỗi x-app-sdk, kiểm tra Super App environment
EOF
# Tạo file .htaccess cho Apache
......@@ -320,11 +308,6 @@ EOF
echo "4. Hoặc sử dụng Node.js: npm install && npm start"
echo ""
echo "🔧 X-App-SDK Integration:"
echo "- Mini app đã được tích hợp x-app-sdk để lấy token và user info từ Super App"
echo "- Super App cần expose window.getToken() và window.getInfo() methods"
echo "- Retry mechanism: Tối đa 3 lần retry cho Super App data"
echo "- Fallback methods: URL parameters và localStorage"
echo "- Xem chi tiết trong X_APP_SDK_INTEGRATION.md"
echo ""
echo "🌐 Test local: cd $EXPORT_DIR && python3 -m http.server 8080"
echo "💡 Tip: Nếu muốn auto-run server, chạy: (cd $EXPORT_DIR && python3 -m http.server 8080 &)"
......
......@@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/get_rx/src/rx_typedefs/rx_typedefs.dart';
import 'package:mypoint_flutter_app/web/web_helper.dart';
import '../configs/constants.dart';
import '../preference/data_preference.dart';
import '../resources/base_color.dart';
......@@ -10,6 +9,7 @@ import '../shared/router_gage.dart';
import '../widgets/alert/custom_alert_dialog.dart';
import '../widgets/alert/data_alert_model.dart';
import '../widgets/alert/popup_data_model.dart';
import 'package:mypoint_flutter_app/web/web_helper.dart';
class AppNavigator {
static final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
......@@ -37,13 +37,13 @@ class AppNavigator {
text: "Đã hiểu",
onPressed: () {
_authDialogShown = false;
if (kIsWeb) {
webCloseApp({
'message': message.isNotEmpty ? message : description,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
return;
}
// if (kIsWeb) {
// webCloseApp({
// 'message': message.isNotEmpty ? message : description,
// 'timestamp': DateTime.now().millisecondsSinceEpoch,
// });
// return;
// }
final phone = DataPreference.instance.phoneNumberUsedForLoginScreen;
if (phone.isNotEmpty) {
Get.offAllNamed(loginScreen, arguments: {'phone': phone});
......
import 'package:flutter/foundation.dart';
class UrlParams {
static String? _token;
static String? _userId;
static String? get token => _token;
static String? get userId => _userId;
static void setToken(String? token) => _token = token;
static void setUserId(String? userId) => _userId = userId;
static Map<String, String?> get allParams => {
'token': token,
'user_id': userId,
};
static bool get hasToken => token != null && token!.isNotEmpty;
static bool get hasUserId => userId != null && userId!.isNotEmpty;
// Helper method để lấy token cho API calls
static String? getTokenForApi() {
return token;
}
// Helper method để lấy userId cho API calls
static String? getUserIdForApi() {
return userId;
}
@override
String toString() => 'UrlParams: $allParams';
}
......@@ -10,8 +10,7 @@ import 'package:mypoint_flutter_app/firebase/push_notification.dart';
import 'package:mypoint_flutter_app/firebase/push_setup.dart';
import 'package:mypoint_flutter_app/base/app_loading.dart';
import 'package:mypoint_flutter_app/env_loader.dart';
import 'package:mypoint_flutter_app/web/web_app_initializer.dart';
import 'package:mypoint_flutter_app/core/deep_link_service.dart';
import 'package:mypoint_flutter_app/web/web_helper.dart';
/// Main app initialization and setup
class AppInitializer {
......@@ -30,13 +29,27 @@ class AppInitializer {
await _initializeFirebase();
// Fetch user point if logged in
await _fetchUserPointIfLoggedIn();
// Initialize web-specific features
await WebAppInitializer.initialize();
// Initialize deep links
await DeepLinkService().initialize();
// Initialize web-specific features (including x-app-sdk)
await _initializeWebFeatures();
print('✅ App initialization completed');
}
/// Initialize web-specific features
static Future<void> _initializeWebFeatures() async {
if (kIsWeb) {
print('🌐 Initializing web-specific features...');
try {
// Initialize x-app-sdk
await webInitializeXAppSDK();
print('✅ Web features initialized successfully');
} catch (e) {
print('❌ Error initializing web features: $e');
}
} else {
print('📱 Skipping web features initialization for mobile');
}
}
/// Initialize Firebase and FCM (mobile only)
static Future<void> _initializeFirebase() async {
if (!kIsWeb) {
......
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart';
......@@ -6,11 +7,15 @@ import 'package:mypoint_flutter_app/resources/base_color.dart';
import 'package:mypoint_flutter_app/screen/splash/splash_screen.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart';
import 'package:mypoint_flutter_app/core/app_initializer.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (kIsWeb) {
setUrlStrategy(PathUrlStrategy());
}
// Initialize all app features
await AppInitializer.initialize();
// Run the app
......
......@@ -4,7 +4,6 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../model/auth/login_token_response_model.dart';
import '../model/auth/profile_response_model.dart';
import '../screen/popup_manager/popup_manager_viewmodel.dart';
import '../web/web_helper_stub.dart';
class DataPreference {
static final DataPreference _instance = DataPreference._internal();
......@@ -78,7 +77,6 @@ class DataPreference {
Future<void> clearLoginToken() async {
_loginToken = null;
webClearStorage();
final prefs = await SharedPreferences.getInstance();
await prefs.remove('login_token');
await PopupManagerViewModel.instance.reset();
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/extensions/string_extension.dart';
import 'package:mypoint_flutter_app/networking/restful_api_viewmodel.dart';
import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart';
import '../../base/base_response_model.dart';
import '../../configs/url_params.dart';
import '../../model/auth/login_token_response_model.dart';
import '../../model/auth/profile_response_model.dart';
import '../../web/web_helper_stub.dart';
import 'models/update_response_model.dart';
import '../../preference/data_preference.dart';
import '../../preference/point/point_manager.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../web/web_helper.dart';
import '../popup_manager/popup_manager_viewmodel.dart';
class SplashScreenViewModel extends RestfulApiViewModel {
......@@ -43,19 +43,58 @@ class SplashScreenViewModel extends RestfulApiViewModel {
_freshUserProfile();
return;
}
final tokenFormWeb = UrlParams.getTokenForApi() ?? "";
print('🔍 SplashScreen - Token from URL: $tokenFormWeb');
if (tokenFormWeb.isEmpty) {
// Try to get token from x-app-sdk first (for web)
String? token = await _getTokenFromSDK();
if (token.orEmpty.isEmpty) {
_directionWhenTokenInvalid();
return;
}
print('✅ Token found, proceeding with login');
LoginTokenResponseModel tokenModel = LoginTokenResponseModel(accessToken: tokenFormWeb);
LoginTokenResponseModel tokenModel = LoginTokenResponseModel(accessToken: token);
await DataPreference.instance.saveLoginToken(tokenModel);
_freshUserProfile();
return;
}
/// Get token from x-app-sdk (web only)
Future<String?> _getTokenFromSDK() async {
if (!kIsWeb) {
print('🔍 SplashScreen - Not on web, skipping SDK token retrieval');
return null;
}
try {
print('🔍 SplashScreen - Attempting to get token from x-app-sdk...');
// Initialize SDK first
await webInitializeXAppSDK();
// Check if SDK is initialized
if (!webIsSDKInitialized()) {
print('⚠️ SplashScreen - SDK not initialized, skipping');
return null;
}
// Get token from SDK
final token = await webGetToken();
if (token != null && token.isNotEmpty) {
print('✅ SplashScreen - Token retrieved from x-app-sdk: ${token.substring(0, 8)}...');
return token;
} else {
final error = webGetLastError();
print('❌ SplashScreen - Failed to get token from SDK: $error');
return null;
}
} catch (e) {
print('❌ SplashScreen - Error getting token from SDK: $e');
return null;
}
}
Future<void> _freshUserProfile() async {
showLoading();
final response = await client.getUserProfile();
......@@ -70,10 +109,9 @@ class SplashScreenViewModel extends RestfulApiViewModel {
}
void _directionWhenTokenInvalid() {
// TODO: handle later
Get.toNamed(onboardingScreen);
// if (kIsWeb) {
// print('❌ No token found on web, cannot proceed');
// print('❌ No token found on web, closing app');
// webCloseApp({
// 'message': 'No token found, cannot proceed',
// 'timestamp': DateTime.now().millisecondsSinceEpoch,
......
import 'package:flutter/foundation.dart';
import 'web_helper.dart';
/// Example usage of closeApp functionality
class CloseAppExample {
/// Close app without returning any data
static void closeAppSimple() {
if (kIsWeb) {
print('🚪 Closing app (simple)...');
webCloseApp();
} else {
print('⚠️ closeApp only works on web platform');
}
}
/// Close app with success data
static void closeAppWithSuccess() {
if (kIsWeb) {
print('🚪 Closing app with success data...');
webCloseApp({
'result': 'success',
'message': 'Operation completed successfully',
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
} else {
print('⚠️ closeApp only works on web platform');
}
}
/// Close app with error data
static void closeAppWithError(String errorMessage) {
if (kIsWeb) {
print('🚪 Closing app with error data...');
webCloseApp({
'result': 'error',
'message': errorMessage,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
} else {
print('⚠️ closeApp only works on web platform');
}
}
/// Close app with custom data
static void closeAppWithCustomData(Map<String, dynamic> customData) {
if (kIsWeb) {
print('🚪 Closing app with custom data...');
webCloseApp({
'result': 'custom',
'data': customData,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
} else {
print('⚠️ closeApp only works on web platform');
}
}
/// Example: Close app after successful payment
static void closeAppAfterPayment({
required String transactionId,
required double amount,
required String currency,
}) {
if (kIsWeb) {
print('🚪 Closing app after payment...');
webCloseApp({
'result': 'payment_success',
'transactionId': transactionId,
'amount': amount,
'currency': currency,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
} else {
print('⚠️ closeApp only works on web platform');
}
}
/// Example: Close app after form submission
static void closeAppAfterFormSubmission({
required String formType,
required Map<String, dynamic> formData,
}) {
if (kIsWeb) {
print('🚪 Closing app after form submission...');
webCloseApp({
'result': 'form_submitted',
'formType': formType,
'formData': formData,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
} else {
print('⚠️ closeApp only works on web platform');
}
}
}
import 'package:flutter/foundation.dart';
import 'package:mypoint_flutter_app/configs/url_params.dart';
import 'web_helper.dart';
/// Web-specific initialization and configuration
class WebAppInitializer {
static final WebAppInitializer _instance = WebAppInitializer._internal();
factory WebAppInitializer() => _instance;
WebAppInitializer._internal();
// Ensure web initialization runs only once per app lifecycle
static bool _didInit = false;
// Ensure we only start one polling sequence for SDK data
static bool _startedSdkPolling = false;
/// Initialize all web-specific features
static Future<void> initialize() async {
if (!kIsWeb) return;
if (_didInit) {
// Prevent re-initialization on hot reload / route changes
return;
}
_didInit = true;
print('🌐 Initializing web-specific features...');
// Handle URL parameters
_handleWebUrlParams();
// Initialize x-app-sdk
_initializeXAppSDK();
}
/// Handle URL parameters for web
static void _handleWebUrlParams() {
print('🔍 Handling web URL parameters...');
final uri = Uri.base;
print('🔍 Current URI: ${uri.toString()}');
final token = uri.queryParameters['token'];
final userId = uri.queryParameters['userId'];
print('🔍 Web URL Params: {token: $token, user_id: $userId}');
if (token != null && token.isNotEmpty) {
UrlParams.setToken(token);
UrlParams.setUserId(userId);
print('✅ Token set from URL: $token');
print('🔍 UrlParams after set: ${UrlParams.allParams}');
// Clean URL to remove query params
webReplaceUrl('/');
} else {
print('❌ No token found in URL parameters');
}
}
/// Initialize x-app-sdk service
static void _initializeXAppSDK() {
print('🔍 Initializing x-app-sdk...');
// Check if x-app-sdk is available from Super App
final isSDKAvailable = webIsSDKAvailable();
print('🔍 XAppSDK available from Super App: $isSDKAvailable');
// Always try to initialize once (no-op on non-web/stub)
webInitializeXAppSDK().then((_) {
print('✅ XAppSDK service initialized');
// Only poll for data if SDK is actually available from host
if (isSDKAvailable) {
if (_startedSdkPolling) return;
_startedSdkPolling = true;
// Wait a bit for JavaScript to initialize and then check for data
_checkForAppHostData(0); // Start with retry count 0
} else {
print('ℹ️ XAppSDK not available – skipping polling outside Super App.');
print('💡 Tip: Test with URL params, e.g. ?token=test123&userId=user123');
}
}).catchError((error) {
print('❌ Error initializing XAppSDK: $error');
});
}
/// Check for app host data with retry mechanism (max 3 retries)
static void _checkForAppHostData(int retryCount) {
print('🔍 Checking for app host data... (attempt ${retryCount + 1}/4)');
// Wait a bit for JavaScript to initialize
Future.delayed(const Duration(milliseconds: 1000), () {
final token = webGetAppHostToken();
final user = webGetAppHostUser();
final error = webGetAppHostError();
final isReady = webIsAppHostDataReady();
print('🔍 Data check result:');
print(' Token: ${token != null ? '***${token.substring(0, 8)}...' : 'null'}');
print(' User: ${user?.toString() ?? 'null'}');
print(' Error: ${error ?? 'null'}');
print(' Ready: $isReady');
if (token != null && token.isNotEmpty) {
print(' Token from Super App: ${token.substring(0, 8)}...');
UrlParams.setToken(token);
if (user != null) {
print(' User from Super App: $user');
UrlParams.setUserId(user['id']?.toString());
// Store user data for later use
webStoreAppHostData(token, user);
}
} else if (error != null) {
print(' Error from Super App: $error');
} else if (!isReady && retryCount < 3) {
print(' App host data not ready yet, will retry... (${retryCount + 1}/3)');
// Retry after a longer delay
Future.delayed(const Duration(milliseconds: 2000), () {
_checkForAppHostData(retryCount + 1);
});
} else if (retryCount >= 3) {
print(' Max retries reached (3), giving up on Super App data');
print('💡 You can test with URL parameters: ?token=test123&user={"id":"user123"}');
} else {
print(' No token found from Super App');
}
});
}
}
export 'web_helper_stub.dart'
if (dart.library.html) 'web_helper_web.dart';
// Stub implementations for non-web platforms
void webReplaceUrl(String path) {
// no-op on non-web
}
void webClearStorage() {
// no-op on non-web
}
/// Get token from app host via x-app-sdk
String? webGetAppHostToken() {
return null;
}
/// Get user info from app host via x-app-sdk
Map<String, dynamic>? webGetAppHostUser() {
return null;
}
/// Check if app host data is ready
bool webIsAppHostDataReady() {
return false;
}
/// Get error message from app host
String? webGetAppHostError() {
return null;
}
/// Initialize x-app-sdk service
/// Initialize x-app-sdk service (no-op on non-web)
Future<void> webInitializeXAppSDK() async {
// no-op on non-web
}
/// Store app host data
void webStoreAppHostData(String token, Map<String, dynamic>? user) {
// no-op on non-web
/// Get token from x-app-sdk (no-op on non-web)
Future<String?> webGetToken() async {
return null;
}
/// Clear app host data
void webClearAppHostData() {
/// Close app and return to Super App (no-op on non-web)
Future<void> webCloseApp([Map<String, dynamic>? data]) async {
// no-op on non-web
}
/// Execute JavaScript in the web context
Future<dynamic> webExecuteJavaScript(String script) async {
return null;
}
/// Call x-app-sdk method if available
Future<dynamic> webCallXAppSDKMethod(String methodName, [List<dynamic>? args]) async {
return null;
/// Check if x-app-sdk is initialized (no-op on non-web)
bool webIsSDKInitialized() {
return false;
}
/// Get user info by key from app host
Future<dynamic> webGetUserInfoByKey(String key) async {
/// Get cached token (no-op on non-web)
String? webGetCachedToken() {
return null;
}
/// Get token asynchronously from app host
Future<String?> webGetTokenAsync() async {
/// Get last error message (no-op on non-web)
String? webGetLastError() {
return null;
}
/// Check if x-app-sdk is available from Super App
bool webIsSDKAvailable() {
return false;
/// Clear token cache (no-op on non-web)
void webClearTokenCache() {
// no-op on non-web
}
/// Close app and return to Super App
void webCloseApp([Map<String, dynamic>? data]) {
/// Reset SDK service (no-op on non-web)
void webResetSDK() {
// no-op on non-web
}
// Web-specific implementations
// Web-specific implementations for x-app-sdk
// ignore: avoid_web_libraries_in_flutter
import 'dart:convert';
import 'package:universal_html/html.dart' as html;
import 'x_app_sdk_service.dart';
void webReplaceUrl(String path) {
try {
final origin = html.window.location.origin;
final newUrl = '$origin$path';
html.window.history.replaceState(null, html.document.title ?? '', newUrl);
} catch (_) {}
}
void webClearStorage() {
try {
html.window.localStorage.clear();
html.window.sessionStorage.clear();
// Optionally clear caches if any
} catch (_) {}
}
/// Get token from app host via x-app-sdk
String? webGetAppHostToken() {
try {
return XAppSDKService().getToken();
} catch (e) {
print('❌ Error getting app host token: $e');
return null;
}
}
/// Get user info from app host via x-app-sdk
Map<String, dynamic>? webGetAppHostUser() {
try {
return XAppSDKService().getUser();
} catch (e) {
print('❌ Error getting app host user: $e');
return null;
}
}
/// Check if app host data is ready
bool webIsAppHostDataReady() {
try {
return XAppSDKService().isServiceReady;
} catch (e) {
print('❌ Error checking app host data ready: $e');
return false;
}
}
/// Get error message from app host
String? webGetAppHostError() {
try {
return XAppSDKService().getErrorMessage();
} catch (e) {
print('❌ Error getting app host error: $e');
return null;
}
}
/// Web-specific helper functions for x-app-sdk integration
/// Initialize x-app-sdk service
Future<void> webInitializeXAppSDK() async {
try {
await XAppSDKService().initialize();
XAppSDKService().listenForUpdates();
} catch (e) {
print('❌ Error initializing x-app-sdk: $e');
}
}
/// Store app host data
void webStoreAppHostData(String token, Map<String, dynamic>? user) {
/// Get token from x-app-sdk
Future<String?> webGetToken() async {
try {
XAppSDKService().storeData(token, user);
return await XAppSDKService().getToken();
} catch (e) {
print('❌ Error storing app host data: $e');
}
}
/// Clear app host data
void webClearAppHostData() {
try {
XAppSDKService().clearData();
} catch (e) {
print('❌ Error clearing app host data: $e');
print('❌ Error getting token: $e');
return null;
}
}
/// Execute JavaScript in the web context
Future<dynamic> webExecuteJavaScript(String script) async {
/// Close app and return to Super App
Future<void> webCloseApp([Map<String, dynamic>? data]) async {
try {
// For now, we'll use a simpler approach
// This method is mainly for future extensibility
print('⚠️ webExecuteJavaScript is not fully implemented yet');
return null;
await XAppSDKService().closeApp(data);
} catch (e) {
print('❌ Error executing JavaScript: $e');
return null;
print('❌ Error closing app: $e');
}
}
/// Call x-app-sdk method if available
Future<dynamic> webCallXAppSDKMethod(String methodName, [List<dynamic>? args]) async {
/// Check if x-app-sdk is initialized
bool webIsSDKInitialized() {
try {
return await XAppSDKService().callSDKMethod(methodName, args);
return XAppSDKService().isInitialized;
} catch (e) {
print('❌ Error calling x-app-sdk method $methodName: $e');
return null;
print('❌ Error checking SDK status: $e');
return false;
}
}
/// Get user info by key from app host
Future<dynamic> webGetUserInfoByKey(String key) async {
/// Get cached token
String? webGetCachedToken() {
try {
return await XAppSDKService().getUserInfo(key);
return XAppSDKService().cachedToken;
} catch (e) {
print('❌ Error getting user info by key $key: $e');
print('❌ Error getting cached token: $e');
return null;
}
}
/// Get token asynchronously from app host
Future<String?> webGetTokenAsync() async {
/// Get last error message
String? webGetLastError() {
try {
return await XAppSDKService().getTokenAsync();
return XAppSDKService().lastError;
} catch (e) {
print('❌ Error getting token async: $e');
print('❌ Error getting last error: $e');
return null;
}
}
/// Check if x-app-sdk is available from Super App
bool webIsSDKAvailable() {
/// Clear token cache
void webClearTokenCache() {
try {
return XAppSDKService().isSDKAvailable();
XAppSDKService().clearToken();
} catch (e) {
print('❌ Error checking SDK availability: $e');
return false;
print('❌ Error clearing token cache: $e');
}
}
/// Close app and return to Super App
void webCloseApp([Map<String, dynamic>? data]) {
/// Reset SDK service
void webResetSDK() {
try {
XAppSDKService().closeApp(data);
XAppSDKService().reset();
} catch (e) {
print('❌ Error closing app: $e');
print('❌ Error resetting SDK: $e');
}
}
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:universal_html/html.dart' as html;
import 'package:universal_html/js_util.dart';
/// X-App-SDK Service for Flutter Web
/// Provides simple integration with x-app-sdk for token retrieval and app closing
class XAppSDKService {
static final XAppSDKService _instance = XAppSDKService._internal();
factory XAppSDKService() => _instance;
XAppSDKService._internal();
String? _token;
Map<String, dynamic>? _user;
bool _isReady = false;
String? _error;
bool _isInitialized = false;
dynamic _sdkModule;
String? _cachedToken;
String? _lastError;
String? get token => _token;
Map<String, dynamic>? get user => _user;
bool get isReady => _isReady;
String? get error => _error;
/// Check if SDK is available and initialized
bool get isInitialized => _isInitialized;
/// Initialize x-app-sdk service and get data from app host
/// Get cached token (if any)
String? get cachedToken => _cachedToken;
/// Get last error message
String? get lastError => _lastError;
/// Initialize the SDK service
Future<void> initialize() async {
if (!kIsWeb) return;
if (!kIsWeb) {
print('⚠️ XAppSDKService: Not running on web platform');
return;
}
try {
// Wait longer for the JavaScript to initialize
await Future.delayed(const Duration(milliseconds: 1000));
// Check if AppHostData is available in window
final appHostData = getProperty(html.window, 'AppHostData');
if (appHostData != null) {
final data = jsonDecode(appHostData.toString());
_token = data['token'];
_user = data['user'] != null ? Map<String, dynamic>.from(data['user']) : null;
_isReady = data['isReady'] ?? false;
_error = data['error'];
print('✅ XAppSDK Service initialized:');
print(' Token: ${_token != null ? '***${_token!.substring(_token!.length - 4)}' : 'null'}');
print(' User: ${_user?.toString() ?? 'null'}');
print(' Ready: $_isReady');
if (_error != null) {
print(' Error: $_error');
}
} else {
print(' AppHostData not found in window, trying fallback...');
await _tryFallbackMethod();
print('🔍 XAppSDKService: Initializing...');
final module = await _loadSdkModule();
if (module == null) {
_lastError = 'x-app-sdk module could not be loaded';
print('❌ XAppSDKService: $_lastError');
return;
}
_sdkModule = module;
_isInitialized = true;
print('✅ XAppSDKService: Initialized successfully');
} catch (e) {
print(' Error initializing XAppSDK Service: $e');
await _tryFallbackMethod();
_lastError = 'Failed to initialize SDK: $e';
print('❌ XAppSDKService: $_lastError');
}
}
/// Fallback method to get data from URL parameters or localStorage
Future<void> _tryFallbackMethod() async {
/// Get token from x-app-sdk
Future<String?> getToken() async {
if (!kIsWeb) {
print('⚠️ XAppSDKService: getToken() called on non-web platform');
return null;
}
if (!_isInitialized) {
print('⚠️ XAppSDKService: SDK not initialized, attempting to initialize...');
await initialize();
if (!_isInitialized) {
_lastError = 'SDK initialization failed';
return null;
}
}
try {
// Try to get from URL parameters first
final uri = Uri.base;
final token = uri.queryParameters['token'];
final userStr = uri.queryParameters['user'];
print('🔍 XAppSDKService: Getting token...');
final sdk = await _loadSdkModule();
if (sdk == null) {
_lastError = 'x-app-sdk not available';
print('❌ XAppSDKService: $_lastError');
return null;
}
if (token != null && token.isNotEmpty) {
_token = token;
if (userStr != null && userStr.isNotEmpty) {
try {
_user = jsonDecode(userStr);
} catch (e) {
print(' Failed to parse user from URL: $e');
}
}
_isReady = true;
print(' Data loaded from URL parameters (fallback)');
return;
final hasGetToken = _hasProperty(sdk, 'getToken');
if (!hasGetToken) {
_lastError = 'getToken method not found in SDK';
print('❌ XAppSDKService: $_lastError');
return null;
}
// Try to get from localStorage
final storedToken = html.window.localStorage['app_host_token'];
final storedUser = html.window.localStorage['app_host_user'];
// Execute getToken method from SDK
final result = await promiseToFuture(callMethod(sdk, 'getToken', []));
if (result != null) {
final status = getProperty(result, 'status')?.toString();
final data = getProperty(result, 'data');
final message = getProperty(result, 'message');
final errorMessage = message?.toString();
if (storedToken != null && storedToken.isNotEmpty) {
_token = storedToken;
if (storedUser != null && storedUser.isNotEmpty) {
try {
_user = jsonDecode(storedUser);
} catch (e) {
print(' Failed to parse user from localStorage: $e');
}
if (status == 'success' && data != null && data.toString().isNotEmpty) {
final tokenString = data.toString();
_cachedToken = tokenString;
_lastError = null;
final preview = tokenString.length > 8 ? tokenString.substring(0, 8) : tokenString;
print('✅ XAppSDKService: Token retrieved successfully: $preview...');
return _cachedToken;
} else {
final details = errorMessage?.isNotEmpty == true ? ' - $errorMessage' : '';
_lastError = 'SDK returned status: $status$details';
print('❌ XAppSDKService: $_lastError');
}
_isReady = true;
print(' Data loaded from localStorage (fallback)');
} else {
print(' No data found in URL parameters or localStorage');
_error = 'No data available from app host';
_lastError = 'getToken returned null';
print('❌ XAppSDKService: $_lastError');
}
} catch (e) {
print(' Error in fallback method: $e');
_error = e.toString();
_lastError = 'Error getting token: $e';
print('❌ XAppSDKService: $_lastError');
}
}
/// Get token from app host
String? getToken() {
return _token;
return null;
}
/// Get user info from app host
Map<String, dynamic>? getUser() {
return _user;
}
/// Close app and return to Super App
Future<void> closeApp([Map<String, dynamic>? data]) async {
if (!kIsWeb) {
print('⚠️ XAppSDKService: closeApp() called on non-web platform');
return;
}
/// Check if service is ready
bool get isServiceReady => _isReady;
if (!_isInitialized) {
print('⚠️ XAppSDKService: SDK not initialized, attempting to initialize...');
await initialize();
if (!_isInitialized) {
print('❌ XAppSDKService: Cannot close app - SDK not initialized');
return;
}
}
/// Get error message if any
String? getErrorMessage() {
return _error;
}
try {
print('🔍 XAppSDKService: Closing app...');
final sdk = await _loadSdkModule();
if (sdk == null) {
print('❌ XAppSDKService: x-app-sdk not available for closeApp');
return;
}
/// Clear stored data
void clearData() {
_token = null;
_user = null;
_isReady = false;
_error = null;
if (kIsWeb) {
try {
html.window.localStorage.remove('app_host_token');
html.window.localStorage.remove('app_host_user');
} catch (e) {
print(' Error clearing localStorage: $e');
final hasCloseApp = _hasProperty(sdk, 'closeApp');
if (!hasCloseApp) {
print('❌ XAppSDKService: closeApp method not found in SDK');
return;
}
}
}
/// Store data for future use
void storeData(String token, Map<String, dynamic>? user) {
_token = token;
_user = user;
_isReady = true;
_error = null;
if (kIsWeb) {
// Execute closeApp method from SDK with optional data
if (data != null) {
callMethod(sdk, 'closeApp', [data]);
print('✅ XAppSDKService: App closed with data: $data');
} else {
callMethod(sdk, 'closeApp', []);
print('✅ XAppSDKService: App closed successfully');
}
} catch (e) {
print('❌ XAppSDKService: Error closing app: $e');
// Fallback: try to close window or go back
try {
html.window.localStorage['app_host_token'] = token;
if (user != null) {
html.window.localStorage['app_host_user'] = jsonEncode(user);
if (html.window.history.length > 1) {
html.window.history.back();
} else {
html.window.close();
}
} catch (e) {
print(' Error storing data: $e');
print('✅ XAppSDKService: Fallback close executed');
} catch (fallbackError) {
print('❌ XAppSDKService: Fallback close also failed: $fallbackError');
}
}
}
/// Listen for data updates from app host
void listenForUpdates() {
if (!kIsWeb) return;
/// Clear cached token
void clearToken() {
_cachedToken = null;
_lastError = null;
print('🧹 XAppSDKService: Token cache cleared');
}
/// Reset service state
void reset() {
_isInitialized = false;
_sdkModule = null;
_cachedToken = null;
_lastError = null;
try {
// Set up a periodic check for updates using js_util
final intervalId = callMethod(html.window, 'setInterval', [
allowInterop(() {
final appHostData = getProperty(html.window, 'AppHostData');
if (appHostData != null) {
final data = jsonDecode(appHostData.toString());
final newToken = data['token'];
final newUser = data['user'];
final newReady = data['isReady'] ?? false;
final newError = data['error'];
final loader = getProperty(html.window, '__xAppSdkLoader');
if (loader != null) {
if (_hasProperty(loader, 'resetCachedModule')) {
callMethod(loader, 'resetCachedModule', []);
}
}
} catch (_) {}
print('🔄 XAppSDKService: Service reset');
}
if (newReady && (newToken != _token || newUser != _user)) {
_token = newToken;
_user = newUser != null ? Map<String, dynamic>.from(newUser) : null;
_isReady = newReady;
_error = newError;
print('🔄 XAppSDK data updated from app host');
}
}
}),
2000 // Check every 2 seconds
]);
// Store interval ID for potential cleanup
print(' Update listener set up with interval ID: $intervalId');
} catch (e) {
print(' Error setting up update listener: $e');
Future<dynamic> _loadSdkModule() async {
if (_sdkModule != null) {
return _sdkModule;
}
// Access loader API exposed in JS
final loader = getProperty(html.window, '__xAppSdkLoader');
if (loader == null) {
_lastError = 'x-app-sdk loader not found on global scope';
print('❌ XAppSDKService: $_lastError');
return null;
}
}
/// Call x-app-sdk method directly from Super App
Future<dynamic> callSDKMethod(String methodName, [List<dynamic>? args]) async {
if (!kIsWeb) return null;
final hasLoadFunction = _hasProperty(loader, 'loadXAppSdkModule');
if (!hasLoadFunction) {
_lastError = 'x-app-sdk loader missing loadXAppSdkModule';
print('❌ XAppSDKService: $_lastError');
return null;
}
try {
// Check if method is available from Super App
final methodExists = getProperty(html.window, methodName);
if (methodExists == null) {
print(' Method $methodName not available from Super App');
final module = await promiseToFuture(callMethod(loader, 'loadXAppSdkModule', []));
if (module == null) {
_lastError = 'x-app-sdk module resolved to null';
print('❌ XAppSDKService: $_lastError');
return null;
}
// Call method directly using callMethod
if (methodName == 'getToken') {
return await promiseToFuture(callMethod(html.window, 'getToken', []));
} else if (methodName == 'getInfo' && args != null && args.isNotEmpty) {
return await promiseToFuture(callMethod(html.window, 'getInfo', [args[0]]));
} else {
print(' Unsupported method: $methodName');
final source = getProperty(module, '__xAppSdkSource');
if (source != null) {
print('🔗 XAppSDKService: Module loaded from $source');
}
final hasGetToken = _hasProperty(module, 'getToken');
final hasCloseApp = _hasProperty(module, 'closeApp');
if (!hasGetToken || !hasCloseApp) {
_lastError = 'x-app-sdk module missing required exports';
print('❌ XAppSDKService: $_lastError');
return null;
}
_sdkModule = module;
return _sdkModule;
} catch (e) {
print(' Error calling SDK method $methodName: $e');
_lastError = 'Failed to load x-app-sdk module: $e';
print('❌ XAppSDKService: $_lastError');
return null;
}
}
/// Get user info by key from Super App
Future<dynamic> getUserInfo(String key) async {
return await callSDKMethod('getInfo', [key]);
}
/// Get token from Super App
Future<String?> getTokenAsync() async {
return await callSDKMethod('getToken');
}
/// Check if x-app-sdk is available from Super App
bool isSDKAvailable() {
if (!kIsWeb) return false;
bool _hasProperty(dynamic target, String name) {
try {
final getToken = getProperty(html.window, 'getToken');
final getInfo = getProperty(html.window, 'getInfo');
return getToken != null && getInfo != null;
} catch (e) {
final value = getProperty(target, name);
return value != null;
} catch (_) {
return false;
}
}
/// Close app and return to Super App
void closeApp([Map<String, dynamic>? data]) {
if (!kIsWeb) return;
try {
print('🚪 Closing app and returning to Super App...');
if (data != null) {
print('📤 Data to return: $data');
}
// Call the JavaScript closeApp function
callMethod(html.window, 'closeApp', [data]);
print(' closeApp called successfully');
} catch (e) {
print(' Error calling closeApp: $e');
// Fallback: try to close the window
try {
html.window.close();
} catch (fallbackError) {
print(' Fallback close also failed: $fallbackError');
}
}
}
}
../acorn/bin/acorn
\ No newline at end of file
../@microsoft/api-extractor/bin/api-extractor
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment