import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:aslan/chatvibe_core/constants/at_global_config.dart'; import 'package:aslan/chatvibe_data/sources/local/user_manager.dart'; import 'package:provider/provider.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:aslan/chatvibe_ui/components/at_debounce_widget.dart'; import 'package:aslan/chatvibe_core/routes/at_fluro_navigator.dart'; import 'package:aslan/chatvibe_data/sources/repositories/room_repository_imp.dart'; import 'package:aslan/chatvibe_managers/rtc_manager.dart'; import 'package:aslan/chatvibe_features/wallet/wallet_route.dart'; import 'package:aslan/chatvibe_features/webview/bridge/at_js_bridge.dart'; class GameRyWebviewPage extends StatefulWidget { final String url; ///安全高度 final double height; final double width; final String gameId; const GameRyWebviewPage({ Key? key, required this.url, required this.height, required this.width, this.gameId = "", }) : super(key: key); @override _GameRyWebviewPageState createState() => _GameRyWebviewPageState(); } class _GameRyWebviewPageState extends State { late WebViewController _controller; bool _isLoading = true; String gameLink = ""; String gameUrl = ""; late final ATJSBridge _bridge; @override void initState() { super.initState(); Provider.of(context, listen: false).closeFullGame = false; _bridge = ATJSBridge(); if (widget.url.split("?").length > 1) { gameLink = "${widget.url}&"; } else { gameLink = "${widget.url}?"; } gameUrl = "${gameLink}sdk=atu&uid=${AccountStorage().getCurrentUser()?.userProfile?.id}&token=${AccountStorage().token}&lang=${ATGlobalConfig.lang}"; _initializeWebViewController(); _bridge.initialize(_controller); _uploadGameRecord(); } @override void dispose() { _controller.loadRequest(Uri.parse('about:blank')); super.dispose(); } @override Widget build(BuildContext context) { return widget.height < 0 ? SizedBox( width: ScreenUtil().screenWidth, height: ScreenUtil().screenHeight, child: buidMainContent(), ) : Column( children: [ Row( children: [ Spacer(), ATDebounceWidget( child: Icon(Icons.close, color: ATGlobalConfig.businessLogicStrategy.getGameWebViewCloseIconColor(), size: 25.w), onTap: () { Navigator.of(context).pop(); }, ), SizedBox(width: 3.w), ], ), SizedBox(height: 6.w), AspectRatio( aspectRatio: (750 / widget.height), child: buidMainContent(), ), ], ); } Widget buidMainContent() { return Scaffold( backgroundColor: ATGlobalConfig.businessLogicStrategy.getGameWebViewScaffoldBackgroundColor(), body: SafeArea( top: false, child: Stack( children: [ Selector( selector: (c, p) => p.closeFullGame, shouldRebuild: (prev, next) => prev != next, builder: (_, closeFullGame, __) { if (closeFullGame) { Navigator.of(context).removeRoute(ModalRoute.of(context)!); } return Container(); }, ), WebViewWidget(controller: _controller), // 加载指示器 if (_isLoading) Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(ATGlobalConfig.businessLogicStrategy.getGameWebViewLoadingIndicatorColor()), ), ), ], ), ), ); } void _onWebResourceError(WebResourceError error) { print('_onWebResourceError:${error.description}'); // 可以在这里添加错误处理逻辑,比如显示错误页面 } void _initializeWebViewController() { _controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setNavigationDelegate( NavigationDelegate( onPageStarted: (url) { setState(() => _isLoading = true); }, onPageFinished: (url) { setState(() => _isLoading = false); _injectBridgeScript(); }, onWebResourceError: (error) { print('Web资源错误: ${error.description}'); _onWebResourceError(error); }, ), ) ..addJavaScriptChannel( 'ATJSBridge', onMessageReceived: (JavaScriptMessage message) { _bridge.handleMessage(message.message); }, ); _bridge.initialize(_controller); // 注册quit和recharge方法 _registerHandlers(); // 加载URL _controller.loadRequest(Uri.parse(gameUrl)); } void _registerHandlers() { // 注册quit方法 _bridge.registerHandler('quit', (data) async { Navigator.of(context).pop(); }); // 注册recharge方法 _bridge.registerHandler('recharge', (data) async { ATNavigatorUtils.push(context, WalletRoute.recharge, replace: false); }); } void _injectBridgeScript() { _controller.runJavaScript(''' // 确保window.quit是一个对象,并有postMessage方法 window.quit = window.quit || {}; window.quit.postMessage = function(message) { // 构造发送给Flutter的消息 const payload = { type: 'call', handlerName: 'quit', data: message, callId: 'web_' + Date.now() }; // 通过JavaScriptChannel发送消息 if (window.JSBridge) { window.JSBridge.postMessage(JSON.stringify(payload)); } }; window.recharge = window.recharge || {}; window.recharge.postMessage = function(message) { // 构造发送给Flutter的消息 const payload = { type: 'call', handlerName: 'recharge', data: message, callId: 'web_' + Date.now() }; // 通过JavaScriptChannel发送消息 if (window.JSBridge) { window.JSBridge.postMessage(JSON.stringify(payload)); } }; console.log('Flutter JS Bridge 初始化完成 - 支持window.quit.postMessage'); '''); } void _uploadGameRecord() { ChatRoomRepository().ludoHistoryRecord( widget.gameId, widget.height > 0 ? "IN_ROOM" : "OUT_ROOM", ); } }