Flutter Web端flutter_inappwebview加载Stripe页面失败问题排查
问题排查:Flutter Web Release模式下flutter_inappwebview加载Stripe页面失败
问题现象
- 使用
flutter_inappwebview: 6.0.0-beta.22包实现App内WebView加载Stripe开户流程,Android模拟器上可正常加载,但Web Release模式(启动命令:flutter run -d chrome --release --web-hostname localhost --web-port 5000)下页面无法加载。 - 控制台报错:
url is: https://connect.stripe.com/setup/c/acct_1MFXNcFwHPwkSrfO/jug2SEGbwzAq NoSuchMethodError: method not found: 'toString' on null js_primitives.dart:47 at a6J.aA (http://localhost:5000/main.dart.js?version=9:56217:6) js_primitives.dart:47 at fY.Gq (http://localhost:5000/main.dart.js?version=9:82446:12) js_primitives.dart:47 at fY.e0 (http://localhost:5000/main.dart.js?version=9:82409:6) js_primitives.dart:47 at IQ.Cc (http://localhost:5000/main.dart.js?version=9:82306:3) js_primitives.dart:47 at IQ.eK (http://localhost:5000/main.dart.js?version=9:82255:16) js_primitives.dart:47 at IQ.e0 (http://localhost:5000/main.dart.js?version=9:82634:8) js_primitives.dart:47 at Ai.Cc (http://localhost:5000/main.dart.js?version=9:82306:3) js_primitives.dart:47 at Ai.eK (http://localhost:5000/main.dart.js?version=9:82255:16) js_primitives.dart:47 at Ai.kE (http://localhost:5000/main.dart.js?version=9:82417:32) js_primitives.dart:47 at Ai.wy (http://localhost:5000/main.dart.js?version=9:82382:6)
- 点击报错中的
js_primitives.dart:47链接时,显示:
Could not load content for org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_primitives.dart (Fetch through target failed: Unsupported URL scheme; Fallback: HTTP error: status code 404, net::ERR_UNKNOWN_URL_SCHEME)
- 初始化时打印的URL是正确的。
相关代码
页面Dart代码
import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:url_launcher/url_launcher_string.dart'; import '../utilities.dart'; class StripeOnboardScreen extends StatefulWidget { final String onboardUrl; const StripeOnboardScreen({Key? key, required this.onboardUrl}) : super(key: key); @override State<StripeOnboardScreen> createState() => _StripeOnboardScreenState(); } class _StripeOnboardScreenState extends State<StripeOnboardScreen> { dynamic backButton = UniversalPlatform.isWeb ? UniversalPlatform.isIOS ? CupertinoIcons.back : Icons.arrow_back : Icons.arrow_back; double fontSize = Utilities.fontSize; final GlobalKey webViewKey = GlobalKey(); InAppWebViewController? webViewController; InAppWebViewSettings settings = InAppWebViewSettings( javaScriptEnabled: true, javaScriptCanOpenWindowsAutomatically: true, useShouldOverrideUrlLoading: true, mediaPlaybackRequiresUserGesture: false, allowsInlineMediaPlayback: true, iframeAllow: "camera; microphone", iframeAllowFullscreen: true); late PullToRefreshController pullToRefreshController; double progress = 0; // Create a webview controller to control the webview and get information // about its current state final urlController = TextEditingController(); @override void initState() { super.initState(); print('url is: ${widget.onboardUrl}'); pullToRefreshController = (kIsWeb ? null : PullToRefreshController( settings: PullToRefreshSettings( color: Colors.blue, ), ))!; } @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, // Show a "Back" button in the app bar if the webview is displaying // a page other than the initial page leading: IconButton( icon: Icon(backButton), color: Colors.redAccent, onPressed: () { Navigator.pop(context); }), ), body: Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: InAppWebView( key: webViewKey, initialUrlRequest: URLRequest( url: WebUri(widget.onboardUrl, forceToStringRawValue: true)), initialSettings: settings, pullToRefreshController: pullToRefreshController, onWebViewCreated: (controller) { webViewController = controller; }, onLoadStart: (controller, url) { setState(() { urlController.text = widget.onboardUrl; }); }, onPermissionRequest: (controller, request) async { return PermissionResponse( resources: request.resources, action: PermissionResponseAction.GRANT); }, shouldOverrideUrlLoading: (controller, navigationAction) async { var uri = navigationAction.request.url!; if (![ "http", "https", "file", "chrome", "data", "javascript", "about" ].contains(uri.scheme)) { await canLaunchUrlString(widget.onboardUrl) ? await launchUrlString(widget.onboardUrl) : throw 'Could not launch ${widget.onboardUrl}'; // and cancel the request return NavigationActionPolicy.CANCEL; } return NavigationActionPolicy.ALLOW; }, onConsoleMessage: (controller, consoleMessage) { print('consoleMessage is : $consoleMessage'); }, ), ), ); } }
index.html配置
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>fixit</title> <!-- <meta name="google-signin-client_id" content="YOUR_GOOGLE_SIGN_IN_OAUTH_CLIENT_ID.apps.googleusercontent.com">--> <meta name="google-signin-client_id" content="147151203258-clupo1g18oueig38uq8nts5mnkgug13e.apps.googleusercontent.com"> <!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">--> <script type="application/javascript" src="/assets/packages/flutter_inappwebview/assets/web/web_support.js" defer></script> </head> <!--<body>--> <body id="app-container"> <!-- The core Firebase JS SDK is always required and must be listed first --> <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script> <!-- TODO: Add SDKs for Firebase products that you want to use https://firebase.google.com/docs/web/setup#available-libraries --> <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-analytics.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-storage.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-remote-config.js"></script> <script> // Your web app's Firebase configuration var firebaseConfig = { apiKey: "xx", authDomain: "xx", databaseURL: "xx", projectId: "xx", storageBucket: "xx", messagingSenderId: "xx", appId: "xx", measurementId: "xx" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); <!-- firebase.analytics();--> </script> <script> if ("serviceWorker" in navigator) { window.addEventListener("load", function () { //navigator.serviceWorker.register("/flutter_service_worker.js"); navigator.serviceWorker.register("/firebase-messaging-sw.js"); }); } </script> <script src="main.dart.js?version=9" type="application/javascript"></script> </body> </html>
Flutter环境信息
[✓] Flutter (Channel stable, 3.3.9, on macOS 12.5.1 21G83 darwin-arm, locale en-IT) • Flutter version 3.3.9 on channel stable at /Users/vincenzocalia/development/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision b8f7f1f986 (3 weeks ago), 2022-11-23 06:43:51 +0900 • Engine revision 8f2221fbef • Dart version 2.18.5 • DevTools version 2.15.0 [✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1) • Android SDK at /Users/vincenzocalia/Library/Android/sdk • Platform android-33, build-tools 32.1.0-rc1 • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 13.4.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 13F100 • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2021.2) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) [✓] VS Code (version 1.73.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension can be installed from: 🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter [✓] Connected device (3 available) • sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 12 (API 31) (emulator) • macOS (desktop) • macos • darwin-arm64 • macOS 12.5.1 21G83 darwin-arm • Chrome (web) • chrome • web-javascript • Google Chrome 108.0.5359.124 HTTP Host availability check is taking a long time...[✓] HTTP Host Availability • All required HTTP hosts are available • No issues found!
排查与修复方案
1. 修复PullToRefreshController空指针问题
Web环境下PullToRefreshController不被支持,当前代码通过强制空断言!将null赋值给late类型的变量,导致后续调用toString时触发空指针错误。
修改代码:
// 将late改为可空类型 PullToRefreshController? pullToRefreshController; @override void initState() { super.initState(); print('url is: ${widget.onboardUrl}'); // 仅在非Web环境初始化控制器 if (!kIsWeb) { pullToRefreshController = PullToRefreshController( settings: PullToRefreshSettings( color: Colors.blue, ), ); } }
同时保持InAppWebView中的pullToRefreshController: pullToRefreshController,不变。
2. 调整WebUri初始化方式
Web环境下使用WebUri(widget.onboardUrl, forceToStringRawValue: true)可能存在兼容性问题,改用标准的解析方式:
initialUrlRequest: URLRequest( url: WebUri.parse(widget.onboardUrl), ),
3. 修正shouldOverrideUrlLoading中的跳转逻辑
当前代码中,非指定scheme的URL会打开初始的Stripe链接,而非当前要跳转的目标URL,修改为:
shouldOverrideUrlLoading: (controller, navigationAction) async { var uri = navigationAction.request.url!; if (![ "http", "https", "file", "chrome", "data", "javascript", "about" ].contains(uri.scheme)) { await canLaunchUrlString(uri.toString()) ? await launchUrlString(uri.toString()) : throw 'Could not launch ${uri.toString()}'; return NavigationActionPolicy.CANCEL; } return NavigationActionPolicy.ALLOW; },
4. 调整web_support.js加载顺序
确保web_support.js在main.dart.js之前加载,去掉defer属性,避免加载顺序问题:
<script type="application/javascript" src="/assets/packages/flutter_inappwebview/assets/web/web_support.js"></script> <!-- 放在main.dart.js之前 --> <script src="main.dart.js?version=9" type="application/javascript"></script>
内容的提问来源于stack exchange,提问作者Vincenzo
相关产品推荐
相关产品推荐

