Flutter 应用内购买与付费墙概述
0:02:00欢迎来到 RevenueCat 的 Flutter SDK Codelab!
目标: 在本 Codelab 中,您将学习如何将 RevenueCat Flutter SDK 集成到新的 Flutter 应用程序中,以获取产品方案并显示预构建的原生付费墙 UI。
您将构建的内容:
一个简单的单屏应用,显示加载指示器,从 RevenueCat 获取您配置的"产品方案",然后显示 PaywallView。
前提条件
在开始之前,您必须完成以下设置:
- Flutter SDK: 您的 Flutter 开发环境应已准备就绪。
- RevenueCat 账户: 在 revenuecat.com 注册的免费账户。
- App Store / Play Store 配置:
- 物理设备或配置好的模拟器: 用于测试应用内购买。
完成本 Codelab 后,您将能够在 Flutter 应用中成功实现应用内购买,并使用 RevenueCat 的 Flutter SDK 显示动态付费墙。
导入 RevenueCat SDK
0:05:00首先,在实现应用内购买之前,您需要将 RevenueCat SDK 导入到您的现有或新项目中。首先,将以下依赖项添加到您的 pubspec.yaml 文件中:
您可以在 GitHub 上查看最新发布版本。
dependencies:
purchases_flutter: 9.2.3
purchases_ui_flutter: 9.2.3添加这些依赖项后(如下图所示),您应该点击"Pub get"按钮,它将自动下载所需的依赖项。
现在,让我们初始化 RevenueCat SDK。我们将在 main.dart 文件中完成此操作。
- 打开
lib/main.dart并用以下代码替换其全部内容。 - 将您的 API 密钥粘贴到
_androidApiKey和_iosApiKey常量中。
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'dart:io' show Platform;
// --- 在此处粘贴您的 REVENUECAT 密钥 ---
const String _androidApiKey = "goog_YOUR_KEY_HERE";
const String _iosApiKey = "appl_YOUR_KEY_HERE";
void main() async {
// 在任何异步插件调用之前确保 Flutter 小部件已初始化
WidgetsFlutterBinding.ensureInitialized();
// 在运行应用之前初始化 RevenueCat SDK
await _initializeRevenueCat();
runApp(const MyApp());
}
Future<void> _initializeRevenueCat() async {
// 为开发启用调试日志
await Purchases.setLogLevel(LogLevel.debug);
PurchasesConfiguration? configuration;
if (Platform.isAndroid) {
configuration = PurchasesConfiguration(_androidApiKey);
} else if (Platform.isIOS) {
configuration = PurchasesConfiguration(_iosApiKey);
} else {
print("此平台未配置 RevenueCat。");
return;
}
try {
await Purchases.configure(configuration);
print("RevenueCat 配置成功!");
} catch (e) {
print("配置 RevenueCat 时出错: $e");
}
}
// ... 后续步骤中的其余代码将放在这里 ...我们刚刚创建了一个 async main 函数。这允许我们 await _initializeRevenueCat 函数,确保在构建任何 UI 之前 RevenueCat SDK 已配置并准备就绪。这是初始化需要从一开始就可用的插件的正确方式。
太棒了!您现在已经完成了 50% 的实现工作。
验证用户权益
0:03:00现在让我们继续验证用户权益。
如前所述,权益(Entitlement)代表用户在购买后解锁的访问级别或功能。这对于确定是否显示广告横幅或授予高级访问权限等场景非常有用。
您可以使用以下代码片段轻松检查用户是否拥有活跃的权益:
const ENTITLEMENT_IDENTIFIER = ".."; // 从您的 RevenueCat 控制台获取特定的权益标识符
final customerInfo = await Purchases.getCustomerInfo();
final isEntitled = customerInfo.entitlements.active[ENTITLEMENT_IDENTIFIER]?.isActive;一旦您检查了用户是否拥有特定权益,就可以根据您应用的商业模式决定如何继续。
例如,如果您的应用是广告支持的,您可能会选择显示或隐藏 AdMob 横幅。或者,您可以选择显示付费墙或购买对话框,让用户解锁高级功能或内容。
以下是如何实现该逻辑的示例:
if (isEntitled == true) {
// 如果用户被授予此权益的访问权限,则不需要显示横幅。
} else {
// 在此处显示横幅 UI 或显示付费墙
..
}实现应用内购买
0:04:00现在,让我们实现应用内购买以提供无广告体验。首先,您需要从 RevenueCat 控制台获取相关产品信息。这些产品数据将用于向用户展示购买选项。
您可以通过调用 Purchases.sharedInstance.awaitGetProducts() 来获取可用产品,如以下示例所示:
// 从 RevenueCat 服务器获取产品信息
final List<StoreProduct> products = await Purchases.getProducts(['paywall_tester.subs']);
// 执行应用内购买
final purchaseResult = await Purchases.purchaseStoreProduct(products.first);如果您提供多种产品变体,例如 paywall_tester.subs:weekly、paywall_tester.subs:monthly 和 paywall_tester.subs:yearly,您可以使用基础产品标识符 paywall_tester.subs 作为 productIds 字段的值来简化产品获取。这将告诉 RevenueCat 以列表形式获取所有相关产品变体,以便您可以在付费墙 UI 中动态展示它们。
一旦您获取了产品数据,就可以通过调用 Purchases.sharedInstance.awaitPurchase(product) 来启动应用内购买流程。这将自动触发 Google Play 购买对话框,使用户能够在您的应用内完成交易。
就这样,您只用几行代码就集成了一个完整功能的应用内购买流程——无需处理收据、商店 API 或手动购买验证的复杂性。
完整的代码示例如下所示:
import 'package:flutter/services.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
/// 通过产品 ID 获取特定产品并启动购买流程。
///
/// 此函数处理潜在错误,如产品未找到、
/// 用户取消购买或其他商店错误。
Future<void> purchaseProduct() async {
// 1. 定义要获取的产品标识符。
const String productId = 'paywall_tester.subs';
try {
// 2. 从 RevenueCat 获取 StoreProduct
print('正在获取产品...');
final List<StoreProduct> products = await Purchases.getProducts([productId]);
// 3. 检查产品列表是否不为空
if (products.isEmpty) {
print('错误:未找到产品。请检查 ID 和您的 RevenueCat 设置。');
// 可选:向用户显示错误消息
return;
}
final StoreProduct productToPurchase = products.first;
print('找到产品: ${productToPurchase.title}。正在启动购买...');
// 4. 启动购买流程
final PurchaseResult purchaseResult = await Purchases.purchaseStoreProduct(productToPurchase);
// 5. 通过验证权益来检查购买是否成功
// 将 "your_premium_entitlement" 替换为您在 RevenueCat 中的实际权益标识符
if (purchaseResult.customerInfo.entitlements.all["your_premium_entitlement"]?.isActive ?? false) {
print('购买成功!用户现在拥有高级访问权限。');
// 授予高级内容访问权限
} else {
print('购买完成,但权益未激活。');
}
} on PlatformException catch (e) {
// 6. 处理潜在错误
final PurchasesErrorCode error = PurchasesErrorHelper.getErrorCode(e);
if (error == PurchasesErrorCode.purchaseCancelledError) {
print('用户取消了购买。');
} else {
print('购买失败,错误: ${e.message}');
}
} catch (e) {
print('发生意外错误: $e');
}
}实现付费墙
0:07:00现在,是时候在 Dart 中实现付费墙了。
步骤 1:创建应用外壳
现在,让我们添加基本的 Flutter 应用结构。这包括 MyApp 小部件和一个有状态的 MyHomePage 小部件,最终将承载我们的付费墙。
将以下代码追加到您的 lib/main.dart 文件中:
// lib/main.dart(续)
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'RevenueCat 付费墙演示',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'RevenueCat 付费墙演示'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
// ... State 类将在下一步中构建 ...步骤 2:获取产品方案并处理状态
这是核心逻辑。在 _MyHomePageState 中,我们将从 RevenueCat 获取产品方案并管理加载和错误状态。
用下面的完整 _MyHomePageState 类替换注释 // ... State 类将在下一步中构建...:
// lib/main.dart(续)
class _MyHomePageState extends State<MyHomePage> {
// 状态变量
Offering? _offering;
bool _isLoading = true;
String? _errorMessage;
@override
void initState() {
super.initState();
// 页面加载后立即获取产品方案
_loadOfferings();
}
Future<void> _loadOfferings() async {
// 我们必须确保在调用 setState 之前小部件仍然挂载
if (!mounted) return;
setState(() {
_isLoading = true;
_errorMessage = null; // 清除之前的错误
});
try {
// 从 RevenueCat 获取当前产品方案
final offerings = await Purchases.getOfferings();
// 确保异步调用后小部件仍然挂载
if (!mounted) return;
setState(() {
_offering = offerings.current;
_isLoading = false;
if (_offering == null) {
_errorMessage = "未找到当前产品方案。请检查您的 RevenueCat 控制台。";
}
});
} catch (e) {
if (!mounted) return;
setState(() {
_isLoading = false;
_errorMessage = "加载产品方案失败: ${e.toString()}";
});
}
}
// build 方法将在最后一步中完成...
@override
Widget build(BuildContext context) {
// 暂时的占位符
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: const Center(child: Text("即将完成!")),
);
}
}- 我们创建了三个状态变量:
_isLoading用于显示加载器,_errorMessage用于显示错误,_offering用于保存付费墙的数据。 - 在
initState中,我们调用_loadOfferings()立即开始数据获取过程。 -
_loadOfferings方法将Purchases.getOfferings()调用包装在try...catch块中。它更新状态以显示/隐藏加载器,并存储获取的产品方案或错误消息。
步骤 3:显示 PaywallView
最后,让我们更新 build 方法,根据我们的状态变量有条件地显示正确的 UI。当产品方案成功加载后,我们将显示 PaywallView。
用以下完整版本替换 _MyHomePageState 中的 build 方法:
// lib/main.dart(在 _MyHomePageState 中)
@override
Widget build(BuildContext context) {
Widget content;
// 获取产品方案时显示加载器
if (_isLoading) {
content = const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 20),
Text("正在加载付费墙..."),
],
);
}
// 如果出现问题,显示错误消息
else if (_errorMessage != null) {
content = Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
_errorMessage!,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.red, fontSize: 16),
),
);
}
// 产品方案加载后显示 PaywallView
else if (_offering != null) {
content = PaywallView(
offering: _offering!,
onDismiss: () => Navigator.pop(context),
onPurchaseCompleted: (customerInfo) {
print("购买完成,权益: ${customerInfo.entitlements.active.keys.first}");
// 您可以在这里关闭或导航离开
},
onPurchaseError: (error) {
print("购买错误: ${error.message}");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("购买失败: ${error.message}")),
);
},
onRestoreCompleted: (customerInfo) {
print("恢复完成。活跃权益: ${customerInfo.entitlements.active.length}");
if (customerInfo.entitlements.active.isNotEmpty) {
Navigator.pop(context);
}
},
);
}
// 不太可能发生的情况的后备方案
else {
content = const Text("没有可用的产品方案。");
}
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: content,
),
);
}完成!
现在,在设备或模拟器上运行您的应用程序。
flutter run- 带有加载器的"正在加载付费墙..."消息。
- 然后是您完整功能的原生付费墙 UI,如下图所示。
故障排除
如果付费墙未显示,请检查调试控制台并查看这些常见问题: "未找到当前产品方案":这是最常见的错误。这意味着您的 RevenueCat 控制台缺少"当前"产品方案。前往产品方案,选择一个产品方案,并确保它被标记为"当前"。 API 密钥错误:仔细检查您是否复制了正在测试的平台的正确公共 API 密钥。 产品未配置:确保您在商店控制台中的应用内产品已正确设置,并在 RevenueCat 中链接到权益和产品方案。 沙盒/测试用户问题:确保您使用沙盒测试账户登录(iOS)或您的电子邮件已添加为许可证测试人员(Android)。
配置完成!现在,每当用户没有所需权益时,您就可以显示付费墙,使用的正是您在付费墙编辑器中配置的设计。
正如您在 Codelab:RevenueCat Google Play 集成(创建付费墙)中所见,付费墙系统建立在服务端驱动的 UI 之上。这意味着您可以直接从控制台动态更新付费墙的内容和设计,无需推送应用更新或经过审核流程。
总结
在本 Codelab 中,您学习了如何集成 RevenueCat 的 Flutter SDK、实现应用内购买以及在 Flutter 中构建付费墙。现在是时候发布您的应用并赚取更多收入了!
您还可以通过以下资源了解更多关于使用 RevenueCat SDK 的信息:
- 产品教程:帮助您入门并充分利用 RevenueCat 的视频教程。