launch method
Implementation
Future<void> launch(
BuildContext context, {
EdgeInsets padding = EdgeInsets.zero,
}) async {
OverlayState? overlayState = Overlay.of(context);
if (userToken.isEmpty) {
print('Error: Token not available.');
return;
}
// If an external teardown removed the overlay without clearing our reference, reset it.
if (overlayEntry != null && overlayEntry?.mounted == false) {
overlayEntry = null;
}
// Prevent multiple overlays from stacking
if (isOverviewVisible) {
return;
}
// Choose WebView host based on the selected environment to avoid token/env mismatches
final String webviewHost = (() {
switch (_environment) {
case 'staging':
return 'webview-staging.promptbet.ai';
case 'dev':
return 'webview-dev.promptbet.ai';
default:
return 'webview.promptbet.ai';
}
})();
overlayEntry = OverlayEntry(
builder: (BuildContext context) {
return SafeArea(
child: Padding(
padding: MediaQuery.of(context).viewInsets.bottom > 0 ? padding.copyWith(bottom: 0) : padding,
child: Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: true,
body: InAppWebView(
initialUrlRequest: URLRequest(
url: WebUri.uri(Uri.https(
webviewHost,
'/',
{
'isNative': 'true',
'token': userToken,
'sdkVersion': sdkVersion,
})),
),
initialSettings: InAppWebViewSettings(
javaScriptEnabled: true,
domStorageEnabled: true,
databaseEnabled: true,
cacheEnabled: true,
clearCache: false,
sharedCookiesEnabled: true,
thirdPartyCookiesEnabled: true,
incognito: false,
disableDefaultErrorPage: false,
supportZoom: false,
builtInZoomControls: false,
displayZoomControls: false,
useWideViewPort: false,
loadWithOverviewMode: false,
hardwareAcceleration: true,
),
onConsoleMessage: (controller, consoleMessage) {
print(consoleMessage);
},
onLoadStop: (controller, url) async {
print("WebView loaded: $url");
await controller.evaluateJavascript(source: """
window.addEventListener('message', function(event) {
if (event.source === window.parent) {
window.flutter_inappwebview.callHandler('onParentMessage', event.data);
}
});
""");
},
onWebViewCreated: (controller) {
_webViewController = controller;
controller.addJavaScriptHandler(
handlerName: 'onParentMessage',
callback: (args) async {
try {
if (args.isEmpty || args[0] == null || args[0] is! Map) {
print("Invalid message payload from window.parent: $args");
return;
}
final dynamic messageData = args[0];
print("Message received from window.parent: $messageData");
if (messageData['type'] == 'close') {
if (messageData['reason'] != 'completed' && isOverviewVisible) {
closeDialog();
}
} else if (messageData['type'] == 'consent') {
await _handleConsent();
} else if (messageData['type'] == 'mic-start') {
// Assume conversationId is always provided by caller
final String conversationId = messageData['conversationId'].toString();
await _startStreaming(conversationId: conversationId);
} else if (messageData['type'] == 'mic-end') {
await _stopStreaming();
} else if (messageData['type'] == 'play-audio') {
final dynamic url = messageData['url'] ?? messageData['src'] ?? messageData['audio'] ?? messageData['value'];
if (url is String && url.isNotEmpty) {
await _playAudioUrl(url);
} else {
print('play-audio event missing valid url');
}
} else if (messageData['type'] == 'stop-audio') {
try {
await _audioPlayer.stop();
await _notifyWeb(
'audioPlaybackStatusFromApp', "false");
} catch (e) {
print('Error stopping audio: $e');
await _notifyWeb(
'audioPlaybackStatusFromApp', "false");
}
} else if (messageData['type'] == 'bet') {
callbackFn(messageData as Map<String, dynamic>);
} else {
callbackFn(messageData as Map<String, dynamic>);
}
} catch (e) {
print('Error handling parent message: $e');
}
},
);
},
),
),
),
);
},
);
overlayState.insert(overlayEntry!);
}