Flutter WebView从WhatsApp返回后白屏(仅Android 14设备)
Android 14下Flutter WebView从WhatsApp返回后空白的解决方案
问题核心分析
你的代码存在两处关键问题,导致仅Android 14设备出现返回后空白现象:
didChangeAppLifecycleState方法语法错误,将initState和dispose错误嵌套在其中,直接导致生命周期监听逻辑完全失效。- 从WhatsApp返回后强制重新加载URL的逻辑,在Android 14的WebView生命周期管控机制下,会触发WebView实例异常销毁与重建,最终显示空白。
修复方案
- 修正生命周期方法结构:将
initState、dispose从didChangeAppLifecycleState中移出,恢复类方法的正确层级。 - 优化状态恢复逻辑:应用从后台恢复时,使用WebView内置的
reload()方法刷新页面,而非重新加载URL,避免实例异常。 - 更新URL启动方式:使用
url_launcher官方API并指定启动模式,确保应用切换后状态正常衔接。
修复后的完整代码
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; const hybridCompositionApiLevel = 29; class WebViewWidget extends StatefulWidget { final int apiVersion; const WebViewWidget({Key? key, this.apiVersion}) : super(key: key); @override _WebViewWidget createState() => _WebViewWidget(); } class _WebViewWidget extends State<WebViewWidget> with WidgetsBindingObserver { String? currentUrl; bool isLoading = true; final _key = UniqueKey(); WebViewController? webViewController; @override Future<void> initState() async { await init(); WidgetsBinding.instance.addObserver(this); super.initState(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed && webViewController != null) { // 使用reload而非重新加载URL,避免WebView实例异常 webViewController!.reload(); } } init() async { if (widget.apiVersion != null && widget.apiVersion >= hybridCompositionApiLevel) { print("hybrid composition"); WebView.platform = SurfaceAndroidWebView(); } } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async => false, child: SafeArea(child: Scaffold(body: _showWebView())), ); } Widget _showWebView() { return Center( child: Stack( children: <Widget>[ WebView( key: _key, initialUrl: '', javascriptMode: JavascriptMode.unrestricted, javascriptChannels: <JavascriptChannel>[ _toasterJavascriptChannel(context), ].toSet(), onPageFinished: (url) async { setState(() { isLoading = false; currentUrl = url; }); print("page finished: $url"); try { var javascript = ''' window.alert = function (e){ Alert.postMessage(e); } '''; await webViewController?.evaluateJavascript(javascript); } catch (_) {} }, navigationDelegate: (NavigationRequest request) async { setState(() { isLoading = false; }); String requestUrl = request.url; print('navigation request: $requestUrl'); if (requestUrl.startsWith("tel:") || requestUrl.startsWith("whatsapp:")) { print('launch external app: $requestUrl'); if (await canLaunchUrl(Uri.parse(requestUrl))) { await launchUrl( Uri.parse(requestUrl), mode: LaunchMode.externalApplication, ); } else { showAlert(context: context, message: "请安装WhatsApp"); } return NavigationDecision.prevent; } currentUrl = requestUrl; return NavigationDecision.navigate; }, onWebViewCreated: (WebViewController controller) async { setState(() { isLoading = false; }); webViewController = controller; // 替换为你的实际初始URL await controller.loadUrl(Constants.mainUrl); }, ), isLoading ? const Center( child: CircularProgressIndicator(), ) : const SizedBox.shrink(), ], ), ); } void showAlert({required BuildContext context, required String message}) { showDialog( context: context, builder: (ctx) => AlertDialog( content: Text(message), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text("确定"), ) ], ), ); } JavascriptChannel _toasterJavascriptChannel(BuildContext context) { return JavascriptChannel( name: 'Alert', onMessageReceived: (JavascriptMessage message) { print("js message received: ${message.message}"); showAlert(context: context, message: message.message); }); } }
额外注意事项
- 确保
url_launcher包已更新到最新版本(在pubspec.yaml中指定最新版本号)。 - Android 14对应用后台状态管控更严格,避免在
resumed状态下执行过重的WebView操作,优先复用现有实例。
内容的提问来源于stack exchange,提问作者Abm
相关产品推荐
相关产品推荐

