Flutter iOS端Stripe支付Sheet如何设置barrierDismissible: false?
解决Flutter iOS端Stripe支付Sheet点击外部关闭的问题
Stripe Flutter SDK的presentPaymentSheet()确实没有直接提供barrierDismissible这类参数,但可以通过两种方式处理iOS端的这个需求:
方法一:拦截并忽略外部点击引发的取消错误
用户点击支付Sheet外部关闭时,会触发StripeException,我们可以在异常捕获逻辑里区分错误类型,只响应用户主动点击Sheet内取消按钮的操作,忽略外部点击的情况。
修改你的displayPaymentSheet()方法:
displayPaymentSheet() async { try { await Stripe.instance.presentPaymentSheet().then((value) async { // 这里处理支付成功后的逻辑 }); } on StripeException catch (e) { // 只在用户主动点击Sheet内的取消按钮时弹出提示 if (e.error.code == 'Canceled') { showDialog( context: context, builder: (_) => const AlertDialog( content: Text("已取消支付"), ) ); } // 外部点击导致的取消错误直接忽略,不做任何提示 } catch (e) { print('支付出错: $e'); } }
方法二:修改iOS原生代码彻底禁止外部点击
Stripe的iOS原生SDK支持调整支付Sheet的弹窗样式,设置为全屏模式后就无法点击外部关闭。需要在Flutter项目的iOS端做以下修改:
- 打开iOS项目的
Runner目录,找到AppDelegate.swift(如果用SwiftUI则是RunnerApp.swift); - 通过自定义MethodChannel扩展原生逻辑,示例如下:
原生Swift代码示例
import UIKit import Flutter import Stripe @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) // 注册MethodChannel,用于Flutter调用原生支付Sheet展示逻辑 let channel = FlutterMethodChannel(name: "com.yourcompany/stripe_payment", binaryMessenger: self.window!.rootViewController!.view.window!.binaryMessenger) channel.setMethodCallHandler { [weak self] call, result in if call.method == "presentPaymentSheet" { guard let clientSecret = call.arguments as? String else { result.error("INVALID_ARGUMENT", "缺少clientSecret", nil) return } self?.presentStripePaymentSheet(clientSecret: clientSecret, result: result) } else { result.notImplemented() } } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func presentStripePaymentSheet(clientSecret: String, result: FlutterResult) { let paymentSheet = STPPaymentSheet(paymentIntentClientSecret: clientSecret) paymentSheet.present(from: self.window?.rootViewController) { stpResult in switch stpResult { case .completed: result(true) case .canceled: result(false) case .failed(let error): result(FlutterError(code: "PAYMENT_FAILED", message: error.localizedDescription, details: nil)) } } // 设置弹窗为全屏模式,禁止外部点击关闭 if let presentedVC = self.window?.rootViewController?.presentedViewController { presentedVC.modalPresentationStyle = .fullScreen } } }
Flutter端调用代码
替换原来的displayPaymentSheet()方法:
displayPaymentSheet() async { const channel = MethodChannel('com.yourcompany/stripe_payment'); try { final result = await channel.invokeMethod('presentPaymentSheet', paymentIntentData!['client_secret']); if (result == true) { // 支付成功逻辑 } else if (result == false) { // 用户主动取消支付,弹出提示 showDialog( context: context, builder: (_) => const AlertDialog( content: Text("已取消支付"), ) ); } } on PlatformException catch (e) { print('支付出错: ${e.message}'); } }
总结
- 不想碰原生代码的话,优先用方法一,通过过滤错误码避免外部点击带来的报错;
- 要彻底禁止外部点击,就需要修改iOS原生代码,将支付Sheet设置为全屏弹窗。
内容的提问来源于stack exchange,提问作者Oliver Mendez
相关产品推荐
相关产品推荐

