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

Flutter WebView无法加载后端返回URL问题求助

Flutter WebView无法加载后端返回的自定义URL,但浏览器可正常打开

我在Flutter应用里用WebView加载后端返回的支付授权URL,WebView始终加载失败,但把这个URL复制到网页浏览器里却能正常打开。以下是相关代码和配置:

WebView实现代码

class MainPaymentScreen extends StatefulWidget {
  const MainPaymentScreen({super.key});

  @override
  State<MainPaymentScreen> createState() => _MainPaymentScreenState();
}

class _MainPaymentScreenState extends State<MainPaymentScreen> {
  String? _url;
  late WebViewController _webViewController;
  late AuthorizationUrl provider;
  bool _hasError = false;

  @override
  void initState() {
    super.initState();
    _webViewController = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setBackgroundColor(Color(0x00000000))
      ..setNavigationDelegate(
        NavigationDelegate(
          onWebResourceError: (error) {
            setState(() {
              _hasError = true;
            });
            Center(child: Text('An error occurred'),);
          },
          onPageFinished: (url) {
            setState(() {
              _hasError = false;
            });
          },
          onNavigationRequest: (request) {
            if (request.url.startsWith('http://www.youtube.com')) {
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    // Access the provider and update the URL
    provider = Provider.of<AuthorizationUrl>(context);
    final newUrl = provider.authorization_url;

    // Load the URL only if it has changed
    if (_url != newUrl && newUrl.isNotEmpty) {
      _url = newUrl;
      print(newUrl);
      _webViewController.loadRequest(Uri.parse(_url!));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: _url == null
            ? Center(child: CircularProgressIndicator())
            : _hasError
                ? Center(child: Text('An error occurred'))
                : WebViewWidget(controller: _webViewController),
      ),
    );
  }
}

获取并存储后端URL的代码

class PaymentService {
  static Future<void> sendPayment(
    BuildContext context, {
    required String amount,
    required String email,
    required String currency,
  }) async {
    http.Response? response;
    try {
      final paymentEndpoint = 'http://10.0.2.2:3000/api/v1/receive-payment';
      response = await http.post(Uri.parse(paymentEndpoint),
          headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ${EnvironConfig.secretKey}',
          },
          body: jsonEncode({
            "amount": amount,
            "email": email,
            "currency": currency,
          }));

      if (response.statusCode == 200) {
        final responseData = jsonDecode(response.body);

        final authUrl = responseData['authorization_url'];
        final reference = responseData['reference'];

        if (authUrl != null && reference != null) {
          Provider.of<AuthorizationUrl>(context, listen: false).authUrl(authUrl);
          // Provider.of<AuthorizationUrl>(context, listen: false).setReference(reference);

          // verifyPayment(reference: reference);

          print("Authorization URL: $authUrl");
          print("Reference: $reference");
        } else {
          print("Authorization URL or Reference is null.");
        }
      } else {
        print("Failed to send payment. Status code: ${response.statusCode}");
        print("Response body: ${response.body}");
      }
    } catch (e) {
      print("Exception occurred: $e");
    }
  }
}

AndroidManifest配置

<uses-permission android:name="android.permission.INTERNET"/>
android:usesCleartextTraffic="true"

排查与解决建议

  1. 打印具体错误信息
    在onWebResourceError里添加错误日志,比如:

    onWebResourceError: (error) {
      setState(() {
        _hasError = true;
      });
      print('WebView加载错误: ${error.description}, 错误码: ${error.errorCode}');
    },
    

    根据错误码能快速定位问题,比如证书错误、连接超时、URL解析失败等。

  2. 确保URL格式正确
    后端返回的URL可能存在未编码的特殊字符,改用Uri.encodeFull解析:

    _webViewController.loadRequest(Uri.parse(Uri.encodeFull(_url!)));
    

    同时检查打印出的URL和浏览器打开的是否完全一致,避免有空格、转义字符等差异。

  3. 修正AndroidManifest配置
    android:usesCleartextTraffic="true"必须放在<application>标签内,正确配置如下:

    <application
        android:label="你的应用名"
        android:icon="@mipmap/ic_launcher"
        android:usesCleartextTraffic="true">
        <uses-permission android:name="android.permission.INTERNET"/>
        <!-- 其他配置 -->
    </application>
    
  4. 调整状态监听逻辑
    改用Consumer监听AuthorizationUrl的状态变化,避免didChangeDependencies触发不及时:

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: SafeArea(
          child: Consumer<AuthorizationUrl>(
            builder: (context, authProvider, child) {
              final newUrl = authProvider.authorization_url;
              if (_url != newUrl && newUrl.isNotEmpty) {
                _url = newUrl;
                _webViewController.loadRequest(Uri.parse(Uri.encodeFull(_url!)));
              }
              return _url == null
                  ? Center(child: CircularProgressIndicator())
                  : _hasError
                      ? Center(child: Text('加载出错'))
                      : WebViewWidget(controller: _webViewController);
            },
          ),
        ),
      );
    }
    
  5. 临时关闭导航拦截测试
    注释掉onNavigationRequest里的拦截逻辑,确认不是URL被误拦截导致无法加载。


内容的提问来源于stack exchange,提问作者Achievers Tv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 16:49:56