You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter WebView从WhatsApp返回后白屏(仅Android 14设备)

Android 14下Flutter WebView从WhatsApp返回后空白的解决方案

问题核心分析

你的代码存在两处关键问题,导致仅Android 14设备出现返回后空白现象:

  • didChangeAppLifecycleState方法语法错误,将initState和dispose错误嵌套在其中,直接导致生命周期监听逻辑完全失效。
  • 从WhatsApp返回后强制重新加载URL的逻辑,在Android 14的WebView生命周期管控机制下,会触发WebView实例异常销毁与重建,最终显示空白。

修复方案

  1. 修正生命周期方法结构:将initState、dispose从didChangeAppLifecycleState中移出,恢复类方法的正确层级。
  2. 优化状态恢复逻辑:应用从后台恢复时,使用WebView内置的reload()方法刷新页面,而非重新加载URL,避免实例异常。
  3. 更新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 20:09:59