Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgefspereira committed Sep 7, 2024
1 parent 7b223ee commit 496a6dd
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 49 deletions.
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ android {
}

dependencies {
implementation 'com.plaid.link:sdk-core:4.5.1'
implementation 'com.plaid.link:sdk-core:4.6.0'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import kotlin.Unit;

import com.plaid.link.Plaid;
import com.plaid.link.PlaidHandler;
import com.plaid.link.SubmissionData;
import com.plaid.link.configuration.LinkTokenConfiguration;
import com.plaid.link.event.LinkEventMetadata;
import com.plaid.link.result.LinkAccount;
Expand All @@ -43,6 +45,9 @@ public class PlaidFlutterPlugin implements FlutterPlugin, MethodCallHandler, Eve
private static final String TOKEN = "token";
private static final String NO_LOADING_STATE = "noLoadingState";

/// SubmissionData
private static final String PHONE_NUMBER = "phoneNumber";

/// LinkResultHandler
private static final String EVENT_ON_SUCCESS = "success";
private static final String EVENT_ON_EXIT = "exit";
Expand All @@ -58,6 +63,7 @@ public class PlaidFlutterPlugin implements FlutterPlugin, MethodCallHandler, Eve
private MethodChannel methodChannel;
private EventChannel eventChannel;
private EventSink eventSink;
private PlaidHandler plaidHandler;

/// Result handler
private final LinkResultHandler resultHandler = new LinkResultHandler(
Expand Down Expand Up @@ -112,15 +118,23 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {

@Override
public void onMethodCall(MethodCall call, @NonNull Result result) {
if(call.method.equals("open")) {
this.open(call.arguments(), result);
}
else if(call.method.equals("close")) {
this.close(result);
}
else {
result.notImplemented();
}
switch (call.method) {
case "create":
this.create(call.arguments(), result);
break;
case "open":
this.open(result);
break;
case "close":
this.close(result);
break;
case "submit":
this.submit(call.arguments(), result);
break;
default:
result.notImplemented();
break;
}
}

/// ActivityAware
Expand Down Expand Up @@ -174,7 +188,7 @@ private void sendEvent(Object argument) {

/// Exposed methods

private void open(Map<String, Object> arguments, Result reply) {
private void create(Map<String, Object> arguments, Result reply) {
if (binding == null) {
reply.error("PlaidFlutter", "Activity not attached", null);
return;
Expand All @@ -193,11 +207,18 @@ private void open(Map<String, Object> arguments, Result reply) {
LinkTokenConfiguration config = getLinkTokenConfiguration(arguments);

if(config != null) {
Plaid.create((Application)context.getApplicationContext(),config).open(binding.getActivity());
reply.success(null);
} else {
reply.error("-1", "Create was not called.", "Unable to create LinkTokenConfiguration");
plaidHandler = Plaid.create((Application)context.getApplicationContext(),config);
}

reply.success(null);
}

private void open(Result reply) {
if (plaidHandler != null) {
plaidHandler.open(binding.getActivity());
}

reply.success(null);
}

private void close(Result reply) {
Expand All @@ -210,6 +231,22 @@ private void close(Result reply) {
reply.success(null);
}

private void submit(Map<String, Object> arguments, Result reply) {
if (arguments == null) {
reply.success(null);
return;
}

String phoneNumber = (String) arguments.get(PHONE_NUMBER);

if (plaidHandler != null && phoneNumber != null) {
SubmissionData submissionData = new SubmissionData(phoneNumber);
plaidHandler.submit(submissionData);
}

reply.success(null);
}

/// Configuration Parsing

private LinkTokenConfiguration getLinkTokenConfiguration(Map<String, Object> arguments) {
Expand Down
31 changes: 24 additions & 7 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:plaid_flutter/plaid_flutter.dart';

void main() => runApp(MyApp());
void main() => runApp(const MyApp());

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

@override
_MyAppState createState() => _MyAppState();
}
Expand Down Expand Up @@ -36,8 +38,8 @@ class _MyAppState extends State<MyApp> {

void _createLinkTokenConfiguration() {
setState(() {
_configuration = LinkTokenConfiguration(
token: "GENERATED_LINK_TOKEN",
_configuration = const LinkTokenConfiguration(
token: "link-sandbox-74cf082e-870b-461f-a37a-038cace0afee",
);
});
}
Expand Down Expand Up @@ -81,14 +83,29 @@ class _MyAppState extends State<MyApp> {
),
ElevatedButton(
onPressed: _createLinkTokenConfiguration,
child: Text("Create Link Token Configuration"),
child: const Text("Create Link Token Configuration"),
),
const SizedBox(height: 15),
ElevatedButton(
onPressed: _configuration != null
? () {
PlaidLink.create(configuration: _configuration!);
PlaidLink.open();
}
: null,
child: const Text("Open"),
),
SizedBox(height: 15),
ElevatedButton(
onPressed: _configuration != null
? () => PlaidLink.open(configuration: _configuration!)
? () {
PlaidLink.submit(
SubmissionData(
phoneNumber: "14155550015",
),
);
}
: null,
child: Text("Open"),
child: const Text("Submit Phone Number"),
),
Expanded(
child: Center(
Expand Down
40 changes: 31 additions & 9 deletions ios/Classes/PlaidFlutterPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#import <LinkKit/LinkKit.h>

static NSString* const kTokenKey = @"token";
static NSString* const kPhoneNumberKey = @"phoneNumber";
static NSString* const kContinueRedirectUriKey = @"redirectUri";
static NSString* const kNoLoadingStateKey = @"noLoadingState";
static NSString* const kOnSuccessType = @"success";
Expand All @@ -19,6 +20,7 @@ @interface PlaidFlutterPlugin () <FlutterStreamHandler>
@implementation PlaidFlutterPlugin {
FlutterEventSink _eventSink;
id<PLKHandler> _linkHandler;
NSError *creationError;
}

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
Expand All @@ -38,12 +40,16 @@ - (void)dealloc {
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"open" isEqualToString:call.method])
[self openWithArguments: call.arguments withResult:result];
if ([@"create" isEqualToString:call.method])
[self createWithArguments: call.arguments withResult:result];
else if ([@"open" isEqualToString:call.method])
[self openWithResult:result];
else if ([@"close" isEqualToString:call.method])
[self closeWithResult:result];
else if([@"resumeAfterTermination" isEqualToString:call.method])
[self resumeAfterTermination:call.arguments withResult:result];
else if([@"submit" isEqualToString:call.method])
[self submit:call.arguments withResult:result];
else
result(FlutterMethodNotImplemented);

Expand Down Expand Up @@ -71,7 +77,7 @@ - (FlutterError *)onCancelWithArguments:(id)arguments {

#pragma mark Exposed methods

- (void) openWithArguments: (id _Nullable)arguments withResult:(FlutterResult)result {
- (void) createWithArguments: (id _Nullable)arguments withResult:(FlutterResult)result {
NSString* token = arguments[kTokenKey];
BOOL noLoadingState = arguments[kNoLoadingStateKey];

Expand Down Expand Up @@ -119,12 +125,17 @@ - (void) openWithArguments: (id _Nullable)arguments withResult:(FlutterResult)re
config.onExit = exitHandler;
config.noLoadingState = noLoadingState;

NSError *creationError = nil;
_linkHandler = [PLKPlaid createWithLinkTokenConfiguration:config error:&creationError];
NSError *error = nil;
_linkHandler = [PLKPlaid createWithLinkTokenConfiguration:config error:&error];
creationError = error;

result(nil);
}

- (void) openWithResult:(FlutterResult)result {
if (_linkHandler) {
__block bool didPresent = NO;

__weak typeof(self) weakSelf = self;
///
void(^presentationHandler)(UIViewController *) = ^(UIViewController *linkViewController) {
UIViewController* rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
Expand All @@ -139,12 +150,11 @@ - (void) openWithArguments: (id _Nullable)arguments withResult:(FlutterResult)re
}
};


[_linkHandler openWithPresentationHandler:presentationHandler dismissalHandler:dismissalHandler];
result(nil);

} else {

NSString *errorMessage = creationError ? creationError.userInfo[@"message"] : @"Create was not called.";
NSString *errorCode = creationError ? [@(creationError.code) stringValue] : @"-1";
NSString *errorDetails = @"Unable to create PLKHandler";
Expand Down Expand Up @@ -185,7 +195,7 @@ - (void) closeWithResult:(FlutterResult)result {
result(nil);
}

- (void) resumeAfterTermination: (id _Nullable)arguments withResult:(FlutterResult)result{
- (void) resumeAfterTermination: (id _Nullable)arguments withResult:(FlutterResult)result{
NSString* redirectUriString = arguments[kContinueRedirectUriKey];
NSURL *redirectUriURL = (id)redirectUriString == [NSNull null] ? nil : [NSURL URLWithString:redirectUriString];

Expand All @@ -196,6 +206,18 @@ - (void) resumeAfterTermination: (id _Nullable)arguments withResult:(FlutterRes
result(nil);
}

- (void) submit: (id _Nullable)arguments withResult:(FlutterResult)result{
NSString* phoneNumber = arguments[kPhoneNumberKey];

if (_linkHandler && phoneNumber) {
PLKSubmissionData *data = [[PLKSubmissionData alloc] init];
data.phoneNumber = phoneNumber;
[_linkHandler submit: data];
}

result(nil);
}

#pragma mark PLKConfiguration

- (PLKLinkTokenConfiguration*)getLinkTokenConfigurationWithToken: (NSString *)token onSuccessHandler:(PLKOnSuccessHandler)successHandler{
Expand Down
27 changes: 23 additions & 4 deletions lib/src/core/link_configuration.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
/// The LinkTokenConfiguration only needs a link_token which is created by your app's
/// server and passed to your app's client to initialize Link. The Link configuration parameters that were
/// previously set within Link itself are now set via parameters passed to /link/token/create and conveyed
/// to Link via the link_token.
class LinkTokenConfiguration {
/// Specify a link_token to authenticate your app with Link. This is a short lived, one-time use token that should be unique for each Link session
final String token;
Expand All @@ -12,6 +8,13 @@ class LinkTokenConfiguration {
/// WEB ONLY: A receivedRedirectUri is required to support OAuth authentication flows when re-launching Link on a mobile device.
final String? receivedRedirectUri;

/// The LinkTokenConfiguration only needs a token which is created by your app's
/// server and passed to your app's client to initialize Link. The Link configuration parameters that were
/// previously set within Link itself are now set via parameters passed to /link/token/create and conveyed
/// to Link via the link_token.
///
/// Note that each time you open Link, you will need to get a new link_token from your server and create a new LinkTokenConfiguration with it.
///
const LinkTokenConfiguration({
required this.token,
this.noLoadingState = false,
Expand All @@ -38,3 +41,19 @@ class LinkTokenConfiguration {
int get hashCode => Object.hash(
token.hashCode, noLoadingState.hashCode, receivedRedirectUri.hashCode);
}

/// Data to submit during a Link session.
class SubmissionData {
/// The end user's phone number.
final String phoneNumber;

SubmissionData({
required this.phoneNumber,
});

Map<String, dynamic> toJson() {
return <String, dynamic>{
'phoneNumber': phoneNumber,
};
}
}
16 changes: 13 additions & 3 deletions lib/src/core/plaid_link.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ class PlaidLink {
static Stream<LinkExit> get onExit =>
_platform.onObject.where((event) => event is LinkExit).cast();

/// Initializes the Plaid Link flow on the device.
static Future<void> open(
/// Creates a handler for Plaid Link. A one-time use object used to open a Link session.
static Future<void> create(
{required LinkTokenConfiguration configuration}) async {
await _platform.open(configuration: configuration);
await _platform.create(configuration: configuration);
}

/// Open Plaid Link by calling open on the Handler object.
static Future<void> open() async {
await _platform.open();
}

/// Closes Plaid Link View
Expand All @@ -46,4 +51,9 @@ class PlaidLink {
static Future<void> resumeAfterTermination(String redirectUri) async {
await _platform.resumeAfterTermination(redirectUri);
}

/// It allows the client application to submit additional user-collected data to the Link flow (e.g. a user phone number) for the Layer product.
static Future<void> submit(SubmissionData data) async {
await _platform.submit(data);
}
}
9 changes: 7 additions & 2 deletions lib/src/platform/plaid_flutter_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ class PlaidFlutterPlugin extends PlaidPlatformInterface {
PlaidPlatformInterface.instance = PlaidFlutterPlugin();
}

/// Initializes the Plaid Link flow on the device.
/// Creates a handler for Plaid Link. A one-time use object used to open a Link session.
@override
Future<void> open({required LinkTokenConfiguration configuration}) async {
Future<void> create({required LinkTokenConfiguration configuration}) async {
WebConfiguration options = WebConfiguration();

/// onSuccess handler
Expand Down Expand Up @@ -77,6 +77,11 @@ class PlaidFlutterPlugin extends PlaidPlatformInterface {
options.env = configuration.token.split('-')[1];

_plaid = await Plaid.create(options);
}

/// Open Plaid Link by calling open on the Handler object.
@override
Future<void> open() async {
_plaid?.open();
}

Expand Down
Loading

0 comments on commit 496a6dd

Please sign in to comment.