Flutter Planning Center API集成插件planningcenter_api的使用
Flutter Planning Center API 集成插件 planningcenter_api
的使用
概述
Planning Center
是一个在线平台,用于教会管理。它提供了多个应用程序,包括签到、服务规划、志愿者管理、CRM、捐赠等。此外,它还提供了一个强大的 API 来远程访问其系统的几乎所有方面。
特性
该包提供了对 JSON:API 规范文档中描述的整个 API 的完全覆盖:
https://api.planningcenteronline.com/[APPLICATION_NAME]/v2/documentation
大多数代码已自动生成,以在 PlanningCenter
API 周围创建 Dart 抽象,并详细记录每个 API 调用。此库实现了截至 2022-03-01 的最新 PlanningCenter
API 版本。
该包支持通过开发人员密钥和密钥进行身份验证:
https://api.planningcenteronline.com/oauth/applications
或者通过 OAuth2 身份验证凭据(请参阅示例以了解详情)。
开始使用
安装包:
dart pub add planningcenter_api
在 Dart 文件中导入包:
import 'package:planningcenter_api/planningcenter_api.dart';
使用
在可以访问 Planning Center
API 之前,需要初始化库。有三种初始化方式:
/// 选项 1: 使用开发人员级别的身份验证,指定 `appId` 和 `secret`。
PlanningCenter.init(appId, secret);
/// 选项 2: 使用 [PlanningCenterCredentials] 对象(可能从文件加载)
PlanningCenter.initWithCredentials(credentials);
/// 选项 3: 等待 OAuth 授权流程
/// [redirector] 是返回 Future<String> 的函数,应该解析为授权码
/// 请参阅示例了解如何在命令行应用程序中执行此操作
await PlanningCenter.authorize(clientId, clientSecret, redirectUrl, scopesList, redirector);
可以通过检查 initialized
静态成员来确定库是否已初始化:
if (PlanningCenter.initialized) {
// 你的代码在这里
}
一旦库初始化完成,你可以直接使用静态成员 PlanningCenter.instance
访问 API 对象,并通过 PlanningCenter.instance.call
发起 API 调用:
/// [data] 对象应为 JSON 字符串或可序列化为 JSON 的对象,将发送到端点。[call] 函数还通过 [PlanningCenterApiQuery] 包装器支持查询变量。
///
/// 此示例将在具有 id [personId] 的人员上附加电子邮件地址 [personEmail]
PlanningCenterApiResponse res = await PlanningCenter.instance.call(
'/people/v2/people/${personId}/emails', {
verb: 'post',
data: {
'data': {
'attributes': {
'address': personEmail,
'location': 'Home',
'primary': true
}
}
},
apiVersion: '2022-01-28',
});
PlanningCenter.instance.call
几乎与 http
客户端的工作方式相同,但它在后台提供了有用的包装器来处理查询变量和身份验证。具体来说,如果需要刷新授权令牌,它会在继续其余 API 调用之前先处理此问题。
虽然可以直接使用 PlanningCenter.instance
发起 API 调用,但推荐的方法是使用库中提供的类关联的方法。这样,你可以在 IDE 中获得代码建议(Intellisense)和文档。
上述向人员添加电子邮件地址的示例应重写如下:
var email = PcoPeopleEmail(personId, address: 'email@example.com', location: 'Home', isPrimary: true);
email.save();
文件上传
你可以将文件上传到 PlanningCenter
以便在其他资源中使用:
var r3 = await PlanningCenterApiFile.upload('myImage.jpg');
if (r3.isError) {
print(r3.errorMessage);
} else {
var f = (r3.data.first);
print('File was successfully uploaded... it can now be attached to other objects by using its UUID');
print('UUID: ${f.id}');
print('CONTENT-TYPE: ${f.contentType}');
}
应用程序资源类
库中的类命名遵循 PlanningCenter
开头的惯例,而资源对象类则遵循 Pco
+ 应用名称 + 资源名称的惯例。例如:PcoPeopleEmail
和 PcoServicesPlan
。
通过输入 Pco
,你的 IDE 应该能够获取可用的资源类。
静态方法
每个可以检索的 PlanningCenter
资源都可通过相关类上的静态方法提供给你。
静态方法始终返回 Future<PcoCollection<T>>
:
PcoCollection<PcoPeopleEmail> emails = await PcoPeopleEmail.getFromPeople(personId);
print(emails.items);
实例关系方法
一旦你拥有 PlanningCenter
资源,你可以通过实例方法获取相关资源。
实例方法始终返回 Future<PcoCollection<T>>
:
PcoCollection<PcoPeopleEmail> emails = await PcoPeopleEmail.getFromPeople(personId);
var myEmail = emails.items.first;
PcoCollection<PcoPeoplePerson> myProfile = await myEmail.getPerson();
print(myProfile.items.first);
包含相关对象
每当 API 对象允许请求相关对象时,这些选项会通过相关方法暴露出来,并且可以通过类型安全的 getter 获取包含的关系。如果 API 将包含作为复数(即 ‘emails’)暴露,则响应将是列表,否则,响应将是可为空的对象。
var collection = await PcoPeoplePerson.get(id: '000000001', includeEmails: true);
var person = collection.items.first;
List<PcoPeopleEmail> emails = person.includedEmails;
PcoPeopleCampus? primaryCampus = person.includedPrimaryCampus;
实例动作方法
每个 PlanningCenter
资源都可以有不同的操作与其关联,并且这些操作可以通过实例方法访问。
- 可以创建或更新的资源暴露了
save
方法。 - 可以删除的资源暴露了
delete
方法。 - 其他操作可能会或可能不会作为相关类的实例方法暴露。
注意:操作应有文档说明,但没有助手代码。你需要发布文档中指定的确切数据。发布的数据应遵循 JSON:API 规范。为此提供了一个 PcoData
类。
var res = await PcoServicesPlan.getFromServiceType('1234567');
if (!res.isError) {
var plan = res.data.first;
var r2 = await plan.itemReorder(
PcoData(
'PlanItemReorder',
attributes: {'sequence': ['5', '1', '3']},
),
);
if (r2.isError) {
print(r2.errorMessage);
} else {
print('success');
}
} else {
print(res.errorMessage);
}
提示
你可以始终通过打印 res.errorMessage
查看 API 请求的错误。
如果你想从头开始获取一些 PlanningCenter
数据,你可能想要调用表示所需数据的类上的静态方法。如果你已经有了一些数据并想获取相关数据,请调用对象本身的方法。
代码应有良好的文档说明。如果你有任何疑问,请创建 GitHub 问题。
示例
import 'dart:async';
import 'dart:io'; // 为了更快地退出脚本
import 'dart:convert'; // 为了美观地打印 JSON
import 'package:planningcenter_api/planningcenter_api.dart';
/// 这里存储我的 [appid], [secret], [oAuthClientId], 和 [oAuthClientSecret] 常量
import '../secrets.dart';
void debug(Object o) {
try {
print(JsonEncoder.withIndent(' ').convert(o));
} on JsonUnsupportedObjectError catch (e) {
print('DEBUG AS STRING BECAUSE: $e');
print(o);
}
}
Future checkResponseError(PlanningCenterApiResponse res) async {
if (res is PlanningCenterApiError) {
debug(res.message);
debug(res.responseBody);
} else {
debug('Success!');
}
}
/// 这里的实际示例代码
void main() async {
// 首先初始化 PlanningCenter API
// 这种初始化将使用开发者 appid 和密钥,从而让你访问开发者可以访问的所有内容
// PlanningCenter.init(appid, secret);
// 这种初始化将使用 OAuth
var credentialsFile = File('credentials.json');
if (await credentialsFile.exists()) {
try {
var credentials = json.decode(await credentialsFile.readAsString());
var creds = PlanningCenterCredentials.fromJson(credentials);
PlanningCenter.initWithCredentials(oAuthClientId, oAuthClientSecret, creds);
} catch (e) {
print(e);
print('无法读取凭证文件');
}
}
if (!PlanningCenter.initialized) {
print('正在使用 OAuth 授权 PlanningCenter');
await PlanningCenter.authorize(
oAuthClientId,
oAuthClientSecret,
PlanningCenter.oAuthScopes,
redirectUri: 'http://localhost:64738/pco_callback',
);
}
if (!PlanningCenter.initialized) {
print('PlanningCenter 身份验证失败');
exit(1);
}
// 现在,所有以 Pco 开头的类都可供使用
/// 获取默认组织的服务类型(默认抓取 25 个)
/// 将返回 List<PcoServicesServiceType>
var collection = await PcoServicesServiceType.get();
debug(collection.response);
if (!collection.isError) {
var service = collection.items.first;
print('找到服务类型: ${service.name}');
/// 大多数类实例都有方法允许你获取相关项目
/// 这次,我们还使用查询对象按降序请求计划的排序日期
var plans = await service.getPlans(
query: PcoServicesPlanQuery(orderBy: PcoServicesPlanOrder.sortDate, reverse: true),
);
if (!plans.isError) {
var plan = plans.items.first;
print('找到计划: ${plan.seriesTitle} - ${plan.title} - ${plan.lastTimeAt}');
var items = await plan.getItems();
for (var item in items.items) {
print('计划项目: ${item.title}\n${item.description}\n');
if (item.title == 'CHANGE ME') {
print('尝试更新此项目');
item.title = 'CHANGED';
var result = await item.save();
print(result.isError ? '失败' : '成功');
}
}
}
} else {
print(collection.error!.message);
}
// 直接调用 API,你可以这样做,但不会返回
// 类型化的数据... 只是一个适度解析的 PlanningCenterApiResponse 对象
var res = await PlanningCenter.instance.call('/services/v2/songs');
checkResponseError(res);
debug(res.toJson());
// 一旦我们完成了客户端,保存凭证文件。这确保了
// 如果在使用客户端期间自动刷新了凭证,新的凭证将在下一次程序运行中可用。
if (PlanningCenter.instance.oAuthCredentials != null) {
await credentialsFile.create(recursive: true);
await credentialsFile.writeAsString(json.encode(PlanningCenter.instance.oAuthCredentials));
}
// var email = PcoPeopleEmail('1', address: 'email@example.com', location: 'Home', isPrimary: true);
// email.save();
// PcoCollection<PcoPeopleEmail> emails = await PcoPeopleEmail.getFromPeople('1');
// var myEmail = emails.data.first;
// var myProfile = await myEmail.getPerson();
// print(myProfile.data.first);
var r = await PcoServicesPlan.getFromServiceType('1234567');
if (!r.isError) {
var plan = r.items.first;
var r2 = await plan.itemReorder(PlanningCenterApiData('PlanItemReorder', attributes: {
'sequence': ['5', '1', '3']
}));
checkResponseError(r2);
debug(r2.toJson());
}
var r3 = await PlanningCenterApiFile.upload('myImage.jpg');
if (r3.isError) {
debug(r3.errorMessage);
debug(r3.responseBody);
} else {
var f = (r3.data.first);
print('文件成功上传...现在可以通过其 UUID 附加到其他对象');
print('UUID: ${f.id}');
print('CONTENT-TYPE: ${f.contentType}');
}
/// 这里是如何向捐赠模块添加捐赠的一个示例
// 首先,添加一个批次
var batch = PcoGivingBatch();
await batch.save();
// 现在创建一个捐赠对象
var don = PcoGivingDonation(
batchId: batch.id!,
personId: '1234',
paymentMethod: 'cash',
paymentCheckNumber: 1234,
paymentSourceId: '289',
receivedAt: DateTime.now(),
);
// 现在使用两个指定项将其保存到服务器
// 注意,不带指定项保存捐赠将失败。
var r4 = await don.saveWithDesignations([
PcoGivingDesignation(
amountCents: 1234,
withRelationships: {
'fund': [PcoGivingFund(id: '1')]
},
),
PcoGivingDesignation(
amountCents: 5678,
withRelationships: {
'fund': [PcoGivingFund(id: '2')]
},
)
]);
checkResponseError(r4);
debug(r4.toJson());
exit(0);
}
更多关于Flutter Planning Center API集成插件planningcenter_api的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter Planning Center API集成插件planningcenter_api的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter项目中集成和使用planningcenter_api
插件的示例代码。这个插件假设你已经有一个有效的Planning Center账户和API令牌。请注意,为了简化示例,这里假设你已经设置好Flutter开发环境并创建了一个新的Flutter项目。
步骤 1: 添加依赖
首先,在你的pubspec.yaml
文件中添加planningcenter_api
依赖。
dependencies:
flutter:
sdk: flutter
planningcenter_api: ^最新版本号 # 请替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
步骤 2: 配置API密钥
为了与Planning Center API进行通信,你需要提供一个有效的API密钥。出于安全考虑,建议不要在代码中硬编码API密钥。你可以使用Flutter的SharedPreferences
或环境变量来管理敏感信息。
步骤 3: 初始化API客户端
在你的Flutter应用中初始化PlanningCenterApi
客户端。这里是一个简单的示例,展示了如何初始化并使用它来获取一些数据。
import 'package:flutter/material.dart';
import 'package:planningcenter_api/planningcenter_api.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PlanningCenterScreen(),
);
}
}
class PlanningCenterScreen extends StatefulWidget {
@override
_PlanningCenterScreenState createState() => _PlanningCenterScreenState();
}
class _PlanningCenterScreenState extends State<PlanningCenterScreen> {
late PlanningCenterApi _apiClient;
@override
void initState() {
super.initState();
_initializeApiClient();
}
Future<void> _initializeApiClient() async {
final SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String apiToken = sharedPreferences.getString('PLANNING_CENTER_API_TOKEN') ?? '';
if (apiToken.isEmpty) {
// Handle the case where the API token is not found
print('API token not found. Please configure it.');
return;
}
_apiClient = PlanningCenterApi(apiToken: apiToken);
// Example: Fetch some data (e.g., people)
_fetchPeople();
}
Future<void> _fetchPeople() async {
try {
final people = await _apiClient.people.list();
print('Fetched people: $people');
// You can update your UI with the fetched data here
} catch (error) {
print('Error fetching people: $error');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Planning Center API Integration'),
),
body: Center(
child: Text('Loading...'), // You can replace this with actual UI widgets once you have the data
),
);
}
}
注意事项
-
API端点和方法:上面的示例使用了
people.list()
方法,这只是一个假设的方法。你需要查阅planningcenter_api
插件的文档,了解可用的端点和方法。 -
错误处理:在实际应用中,你需要更细致地处理API调用中的错误,比如网络错误、认证错误等。
-
安全性:确保你的API密钥安全存储,不要在客户端代码中硬编码敏感信息。
-
UI更新:上面的示例中并没有更新UI,因为示例重点在于API集成。在实际应用中,你需要在获取数据后更新UI以显示数据。
-
文档:查阅Planning Center API文档和
planningcenter_api
插件的文档,以获取更多信息和示例。
这个示例提供了一个基本的框架,你可以在此基础上根据你的需求进行扩展和定制。